c++reflectionconstexprcompile-timec++-faq

How can I obtain a C++ expression's type, at compile time (i.e. constexpr'ly)?


I would like to use the name of a type at compile time. For example, suppose I've written:

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

and now I want to have:

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

But alas, typeid(T).name() is just const char*, not constexpr... is there some other, constexpr way to get a type's name?


Solution

  • Edit: Updated based on this answer to the non-constexpr-specific question; it is the result of refinements by several people including @HowardHinnant, @康桓瑋 @Val and myself.

    The C++ language standard, up to and including C++17, does not provide any facility for obtaining type names. So, we resort to compiler-specific approaches. This works with GCC, clang and 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);
    }