我能否以 constexpr 方式获取C++类型名称

Can I obtain C++ type names in a constexpr way?

本文关键字:C++ 类型 获取 方式 constexpr      更新时间:2023-10-16

我想在编译时使用类型的名称。例如,假设我写了:

constexpr size_t my_strlen(const char* s)
{
        const char* cp = s;
        while(*cp != '') { cp++; };
        return cp - s;
}

现在我想拥有:

template <typename T>
constexpr auto type_name_length = my_strlen(typeid(T).name());

但可惜的是,typeid(T).name()只是const char*,而不是阴谋...有没有其他的,constexpr方法来获取类型的名称?

嗯,你可以,有点,但可能不是很便携:

struct string_view
{
    char const* data;
    std::size_t size;
};
inline std::ostream& operator<<(std::ostream& o, string_view const& s)
{
    return o.write(s.data, s.size);
}
template<class T>
constexpr string_view get_name()
{
    char const* p = __PRETTY_FUNCTION__;
    while (*p++ != '=');
    for (; *p == ' '; ++p);
    char const* p2 = p;
    int count = 1;
    for (;;++p2)
    {
        switch (*p2)
        {
        case '[':
            ++count;
            break;
        case ']':
            --count;
            if (!count)
                return {p, std::size_t(p2 - p)};
        }
    }
    return {};
}

您可以将所需的type_name_length定义为:

template <typename T>
constexpr auto type_name_length = get_name<T>().size;

DEMO(适用于 clang & g++)

编辑:根据对非constexpr特定问题的回答进行了更新;这是包括@HowardHinnant,@康桓瑋 @Val和我在内的几个人改进的结果。

我所知,语言标准不提供任何获取类型名称的工具。因此,我们诉诸特定于编译器的方法。这适用于GCC,clang和MSVC。

#include <string_view>
// If you can't use C++17's standard library, you'll need to use the GSL 
// string_view or implement your own struct (which would not be very difficult,
// since we only need a few methods here)
template <typename T> constexpr std::string_view type_name();
template <>
constexpr std::string_view type_name<void>()
{ return "void"; }
namespace detail {
using type_name_prober = void;
template <typename T>
constexpr std::string_view wrapped_type_name() 
{
#ifdef __clang__
    return __PRETTY_FUNCTION__;
#elif defined(__GNUC__)
    return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
    return __FUNCSIG__;
#else
#error "Unsupported compiler"
#endif
}
constexpr std::size_t wrapped_type_name_prefix_length() { 
    return wrapped_type_name<type_name_prober>().find(type_name<type_name_prober>()); 
}
constexpr std::size_t wrapped_type_name_suffix_length() { 
    return wrapped_type_name<type_name_prober>().length() 
        - wrapped_type_name_prefix_length() 
        - type_name<type_name_prober>().length();
}
} // namespace detail
template <typename T>
constexpr std::string_view type_name() {
    constexpr auto wrapped_name = detail::wrapped_type_name<T>();
    constexpr auto prefix_length = detail::wrapped_type_name_prefix_length();
    constexpr auto suffix_length = detail::wrapped_type_name_suffix_length();
    constexpr auto type_name_length = wrapped_name.length() - prefix_length - suffix_length;
    return wrapped_name.substr(prefix_length, type_name_length);
}

可以在模板中使用的替代答案,现在它可以与 g++ 和 clang++ 和 msvc 一起运行。

修改自上面的答案@einpoklum:https://stackoverflow.com/a/56600402/12529885

#include <iostream>
#include <string_view>
template<typename T>
struct TypeName {
    constexpr static std::string_view fullname_intern() {
        #if defined(__clang__) || defined(__GNUC__)
            return __PRETTY_FUNCTION__;
        #elif defined(_MSC_VER)
            return __FUNCSIG__;
        #else
            #error "Unsupported compiler"
        #endif
    }
    constexpr static std::string_view name() {
        size_t prefix_len = TypeName<void>::fullname_intern().find("void");
        size_t multiple   = TypeName<void>::fullname_intern().size() - TypeName<int>::fullname_intern().size();
        size_t dummy_len  = TypeName<void>::fullname_intern().size() - 4*multiple;
        size_t target_len = (fullname_intern().size() - dummy_len)/multiple;
        std::string_view rv = fullname_intern().substr(prefix_len, target_len);
        if (rv.rfind(' ') == rv.npos)
            return rv;
        return rv.substr(rv.rfind(' ')+1);
    }
    using type = T;
    constexpr static std::string_view value = name();
};
namespace s1 {
    class MyClass;
}
//Both MSVC, G++ and Clang++ have passed test.
int main () {
    static_assert(TypeName<s1::MyClass>::value == "s1::MyClass");
    std::cout<<"FULLNAME> "<<TypeName<void>::fullname_intern()<<std::endl;
    std::cout<<"TYPETEST> '"<<TypeName<s1::MyClass>::value<<"' == 's1::MyClass'"<<std::endl;
    return 0;
}

请注意:

Clang++中的全名:static std::string_view TypeName<void>::fullname_intern() [T = void]

G++ 中的全名:static constexpr std::string_view TypeName<T>::fullname_intern() [with T = void; std::string_view = std::basic_string_view<char>]

MSVC 中的全名:class std::basic_string_view<char,struct std::char_traits<char> > __cdecl TypeName<void>::fullname_intern(void)(但这里的"类 s1::MyClass"而不是"s1::MyClass")