Могу ли я получить имена типов 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;
}