Могу ли я получить имена типов C++ способом constexpr?

Я хотел бы использовать имя типа во время компиляции. Например, предположим, я написал:

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

и теперь я хочу иметь:

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

Но увы, typeid(T).name() просто const char*, не constexpr... есть какой-то другой, constexpr способ получить имя типа?

3 ответа

Решение

Ну, вы могли бы вроде как, но, вероятно, не совсем переносимо:

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;

ДЕМО (работает для clang & g++)

(Основано на сущности @melak47 и использовании C++17):

#using <string_view>
// and if it's not C++17, take the GSL implementation or just roll your own struct - 
// you only need to implement 3 or 4 methods here.

namespace detail {

template<typename T>
constexpr const char* templated_function_name_getter() {
#if defined(__GNUC__) || defined(__clang__)
    return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
    return __FUNCSIG__;
#else
#error unsupported compiler (only GCC, clang and MSVC are supported)
#endif
}

} // namespace detail

template<typename T>
constexpr std::string_view type_name() {

    constexpr std::string_view funcsig =  detail::templated_function_name_getter<T>();

    // Note: The "magic numbers" below 

    #if defined(__GNUC__) || defined(__clang__)
    constexpr auto start_bit = std::string_view{"T = "};
    constexpr auto end_bit = std::string_view{"]"};
    #elif defined(_MSC_VER)
    constexpr auto start_bit = std::string_view{"detail::templated_function_name_getter<"};
    constexpr auto end_bit = std::string_view{">("};
    #else
    #error unsupported compiler (only GCC, clang and MSVC are supported)
    #endif

    constexpr auto start = funcsig.find(start_bit);
    constexpr auto end = funcsig.rfind(end_bit);
    static_assert(
        start != funcsig.npos and end != funcsig.npos and end > start, 
        "Failed parsing the __PRETTY_FUNCTION__/__FUNCSIG__ string");
    }
    return funcsig.substr(start + start_bit.size(), end - start - start_bit.size());
}

Примечание. Теперь здесь реализована более приятная версия этого подхода.

Как я могу отредактировать этот код, чтобы пройти проверку в Clang?
Альтернативный ответ, который можно использовать в шаблоне (только для GCC).
Изменено из ответа @einpoklum выше: /questions/34866013/mogu-li-ya-poluchit-imena-tipov-c-sposobom-constexpr/50456911#50456911

      #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 suffix_len = TypeName<void>::fullname_intern().size() - prefix_len - 4;
        size_t target_len = fullname_intern().size() - prefix_len - suffix_len;
        return fullname_intern().substr(prefix_len, target_len);
    }

    using type = T;
    constexpr static std::string_view value = name();

};

namespace s1 {
    class MyClass;
}

int main () {
    //Pass in G++8.4.1, failure by Clang++11
    static_assert(TypeName<s1::MyClass>::value == "s1::MyClass");

    std::cout<<TypeName<s1::MyClass>::name()<<std::endl;
    return 0;
}
Другие вопросы по тегам