我想在编译时使用类型的名称。例如,假设我写了:
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方法来获取类型的名称吗?
嗯,你可以,有点,但可能不太便携:
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)
(但这里的“class s1::MyClass”不是“s1::MyClass”)