Por ejemplo:¿Es posible imprimir el tipo de una variable en C++ estándar?
int a = 12;
cout << typeof(a) << endl;
Resultados previstos:
int
Por ejemplo:¿Es posible imprimir el tipo de una variable en C++ estándar?
int a = 12;
cout << typeof(a) << endl;
Resultados previstos:
int
C++ 11 actualización a una pregunta muy antigua: Tipo de impresión variable en C++.
La respuesta aceptada (y buena) es usar typeid(a).name()
, donde a
es un nombre de variable.
Ahora en C++ 11 tenemos decltype(x)
, que puede convertir una expresión en un tipo. Y decltype()
viene con su propio conjunto de reglas muy interesantes. Por ejemplo, decltype(a)
y decltype((a))
generalmente serán tipos diferentes (y por razones buenas y comprensibles una vez que esos motivos están expuestos).
¿Nos ayudará nuestro confiable typeid(a).name()
a explorar este mundo nuevo y valiente?
Nº
Pero la herramienta que no es tan complicado. Y es esa herramienta que estoy usando como respuesta a esta pregunta. Voy a comparar y contrastar esta nueva herramienta al typeid(a).name()
. Y esta nueva herramienta en realidad está construida en la parte superior de typeid(a).name()
.
La cuestión fundamental:
typeid(a).name()
tira a la basura CV-calificadores, referencias y lvalue/rvalue-dad. Por ejemplo:
const int ci = 0;
std::cout << typeid(ci).name() << '\n';
Para mí salidas:
i
y supongo que en las salidas de MSVC:
int
es decir, el const
se ha ido. Esto no es un problema de QOI (Quality Of Implementation). El estándar ordena este comportamiento.
Lo que estoy recomendando a continuación es:
template <typename T> std::string type_name();
que se utilizaría como esto:
const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';
y para mí salidas:
int const
<disclaimer>
No he probado esto en MSVC. </disclaimer>
Pero agradezco los comentarios de quienes lo hacen.
La solución C++ 11
estoy usando __cxa_demangle
para plataformas que no son de MSVC como recomendar por ipapadop en su respuesta a demangle tipos. Pero en MSVC confío en typeid
para exigir nombres (no probados). Y este núcleo se basa en algunas pruebas sencillas que detectan, restauran e informan cv-calificadores y referencias al tipo de entrada.
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
Los resultados
Con esta solución que pueda hacer esto:
int& foo_lref();
int&& foo_rref();
int foo_value();
int
main()
{
int i = 0;
const int ci = 0;
std::cout << "decltype(i) is " << type_name<decltype(i)>() << '\n';
std::cout << "decltype((i)) is " << type_name<decltype((i))>() << '\n';
std::cout << "decltype(ci) is " << type_name<decltype(ci)>() << '\n';
std::cout << "decltype((ci)) is " << type_name<decltype((ci))>() << '\n';
std::cout << "decltype(static_cast<int&>(i)) is " << type_name<decltype(static_cast<int&>(i))>() << '\n';
std::cout << "decltype(static_cast<int&&>(i)) is " << type_name<decltype(static_cast<int&&>(i))>() << '\n';
std::cout << "decltype(static_cast<int>(i)) is " << type_name<decltype(static_cast<int>(i))>() << '\n';
std::cout << "decltype(foo_lref()) is " << type_name<decltype(foo_lref())>() << '\n';
std::cout << "decltype(foo_rref()) is " << type_name<decltype(foo_rref())>() << '\n';
std::cout << "decltype(foo_value()) is " << type_name<decltype(foo_value())>() << '\n';
}
y la salida es:
decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast<int&>(i)) is int&
decltype(static_cast<int&&>(i)) is int&&
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int&
decltype(foo_rref()) is int&&
decltype(foo_value()) is int
Nota (por ejemplo) la diferencia entre decltype(i)
y decltype((i))
. El primero es el tipo de la declaración de i
. Este último es el "tipo" de la expresión i
. (las expresiones nunca tienen tipo de referencia, pero como una convención decltype
representa expresiones lvalue con referencias lvalue).
Por lo tanto, esta herramienta es un excelente vehículo para aprender acerca de decltype
, además de explorar y depurar su propio código.
Por el contrario, si yo fuera a construir esta sólo en typeid(a).name()
, sin añadir perdidos CV-calificadores o referencias, la salida sería:
decltype(i) is int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast<int&>(i)) is int
decltype(static_cast<int&&>(i)) is int
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(foo_value()) is int
es decir, Cada referencia y cv-qualifier se elimina.
C++ 14 Actualización
Justo cuando crees que tienes una solución a un problema clavado, siempre hay alguien que sale de la nada y que muestra una manera mucho mejor. :-)
This answer de Jamboree muestra cómo obtener el nombre del tipo en C++ 14 en tiempo de compilación. Es una solución brillante por un par de razones:
Jamboree'sanswer no deja todo en claro para VS, y estoy modificando su código un poco. Pero dado que esta respuesta tiene muchos puntos de vista, tómese un tiempo para ir allí y votar su respuesta, sin la cual, esta actualización nunca hubiera sucedido.
#include <cstddef>
#include <stdexcept>
#include <cstring>
#include <ostream>
#ifndef _MSC_VER
# if __cplusplus < 201103
# define CONSTEXPR11_TN
# define CONSTEXPR14_TN
# define NOEXCEPT_TN
# elif __cplusplus < 201402
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN
# define NOEXCEPT_TN noexcept
# else
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN constexpr
# define NOEXCEPT_TN noexcept
# endif
#else // _MSC_VER
# if _MSC_VER < 1900
# define CONSTEXPR11_TN
# define CONSTEXPR14_TN
# define NOEXCEPT_TN
# elif _MSC_VER < 2000
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN
# define NOEXCEPT_TN noexcept
# else
# define CONSTEXPR11_TN constexpr
# define CONSTEXPR14_TN constexpr
# define NOEXCEPT_TN noexcept
# endif
#endif // _MSC_VER
class static_string
{
const char* const p_;
const std::size_t sz_;
public:
typedef const char* const_iterator;
template <std::size_t N>
CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN
: p_(a)
, sz_(N-1)
{}
CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN
: p_(p)
, sz_(N)
{}
CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;}
CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;}
CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;}
CONSTEXPR11_TN const_iterator end() const NOEXCEPT_TN {return p_ + sz_;}
CONSTEXPR11_TN char operator[](std::size_t n) const
{
return n < sz_ ? p_[n] : throw std::out_of_range("static_string");
}
};
inline
std::ostream&
operator<<(std::ostream& os, static_string const& s)
{
return os.write(s.data(), s.size());
}
template <class T>
CONSTEXPR14_TN
static_string
type_name()
{
#ifdef __clang__
static_string p = __PRETTY_FUNCTION__;
return static_string(p.data() + 31, p.size() - 31 - 1);
#elif defined(__GNUC__)
static_string p = __PRETTY_FUNCTION__;
# if __cplusplus < 201402
return static_string(p.data() + 36, p.size() - 36 - 1);
# else
return static_string(p.data() + 46, p.size() - 46 - 1);
# endif
#elif defined(_MSC_VER)
static_string p = __FUNCSIG__;
return static_string(p.data() + 38, p.size() - 38 - 7);
#endif
}
Este código se auto-retroceso en la constexpr
si todavía está atascado en la antigua C++ 11. Y si estás pintando en la pared de la cueva con C++ 98/03, también se sacrificará el noexcept
.
C++ 17 Actualización
En los comentarios a continuación Lyberta señala que el nuevo std::string_view
puede reemplazar static_string
:
template <class T>
constexpr
std::string_view
type_name()
{
using namespace std;
#ifdef __clang__
string_view p = __PRETTY_FUNCTION__;
return string_view(p.data() + 34, p.size() - 34 - 1);
#elif defined(__GNUC__)
string_view p = __PRETTY_FUNCTION__;
# if __cplusplus < 201402
return string_view(p.data() + 36, p.size() - 36 - 1);
# else
return string_view(p.data() + 49, p.find(';', 49) - 49);
# endif
#elif defined(_MSC_VER)
string_view p = __FUNCSIG__;
return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}
He actualizado las constantes para VS gracias a la muy agradable trabajo detectivesco por Jive Dadson en los comentarios a continuación.
VS 14 CTP imprimieron los tipos correctos, solo tuve que agregar una línea '#include
Esta función es muy útil. ¡Gracias por compartir! – alecov
¿Por qué plantilla
Probar:
#include <typeinfo>
// …
std::cout << typeid(a).name() << '\n';
Puede que tenga que activar RTTI en las opciones del compilador para que esto funcione. Además, la salida de esto depende del compilador. Puede ser un nombre de tipo sin formato o un símbolo de cambio de nombre o cualquier elemento intermedio.
¿Por qué la cadena devuelta por la función name() es la implementación definida? – Destructor
@PravasiMeet No hay una buena razón, que yo sepa. El comité simplemente no quería forzar a los implementadores de compiladores a instrucciones técnicas particulares, probablemente un error, en retrospectiva. –
¿Hay alguna bandera que pueda usar para habilitar RTTI? Tal vez podrías hacer tu respuesta inclusiva. – Jim
No se olvide de incluir <typeinfo>
Creo que lo que se está refiriendo a es la identificación del tipo de tiempo de ejecución. Puedes lograr lo anterior haciendo.
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
int i;
cout << typeid(i).name();
return 0;
}
Puede usar una clase de rasgos para esto. Algo así como:
#include <iostream>
using namespace std;
template <typename T> class type_name {
public:
static const char *name;
};
#define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
#define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)
DECLARE_TYPE_NAME(int);
int main()
{
int a = 12;
cout << GET_TYPE_NAME(a) << endl;
}
El DECLARE_TYPE_NAME
definen existe para hacer la vida más fácil en la declaración de esta clase rasgos de todos los tipos que se pueden esperar a necesitar.
Esto podría ser más útil que las soluciones que implican typeid
porque puede controlar la salida. Por ejemplo, usar typeid
para long long
en mi compilador da "x".
Puede usar plantillas.
template <typename T> const char* typeof(T&) { return "unknown"; } // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }
En el ejemplo anterior, cuando el tipo no coincida, se imprimirá "desconocido".
¿No imprime "int" para pantalones cortos y caracteres? Y "flotar" para dobles? – gartenriese
@gartenriese La especialización no tiene ese inconveniente. Para 'double' compilaría la versión no especializada de la función de plantilla en lugar de hacer una conversión de tipo implícita para usar la especialización: http://cpp.sh/2wzc – chappjc
@chappjc: Honestamente, no sé por qué pregunté que en ese entonces, ahora es bastante claro para mí. Pero gracias por responder una pregunta de un año de todos modos! – gartenriese
Tenga en cuenta que los nombres generados por la característica RTTI de C++ son no portátiles. Por ejemplo, la clase
MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>
tendrán los siguientes nombres:
// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE
lo que no puede utilizar esta información para la serialización. Pero aún así, la typeid (a) .name (a) Propiedad todavía se puede utilizar para fines de registro/depuración
Las otras respuestas que implican RTTI (typeid) son probablemente lo que usted desee, siempre y cuando:
la alternativa, (similar a la respuesta de Greg Hewgill), es la construcción de un tiempo de compilación tabla de rasgos
template <typename T> struct type_as_string;
// declare your Wibble type (probably with definition of Wibble)
template <>
struct type_as_string<Wibble>
{
static const char* const value = "Wibble";
};
en cuenta que si usted envuelve las declaraciones en una macro, que tendrá problemas para declarar los nombres de tipos de plantillas que tienen más de un parámetro (por ejemplo,std :: map), debido a la coma.
Para acceder al nombre del tipo de una variable, todo lo que necesita es
template <typename T>
const char* get_type_as_string(const T&)
{
return type_as_string<T>::value;
}
¡Buen punto sobre la coma, sabía que había una razón por la que las macros eran una mala idea pero no pensé en eso en ese momento! –
static const char * value = "Wibble"; no puedes hacer eso mate :) –
muy feo, pero hace el truco si solo deseas compilar información de tiempo (por ejemplo, para la depuración):
auto testVar = std::make_tuple(1, 1.0, "abc");
static_assert(decltype(testVar)::dummy_error, "DUMP MY TYPE");
Devuelve:
Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:5:19: error: 'dummy_error' is not a member of 'std::tuple<int, double, const char*>'
solo C++ podría hacer esto tan difícil (imprimir un tipo de variables automáticas en tiempo de compilación). SOLO C++. – KarlP
¡Felicitaciones por usar un error como característica de C++! –
@KarlP Bueno, para ser justos, es un poco intrincado, esto también funciona :) '' 'auto testVar = std :: make_tuple (1, 1.0," abc "); decltype (testVar) :: foo = 1; '' ' – NickV
Como se ha mencionado, typeid().name()
pueden devolver un nombre revuelto. En GCC (y algunos otros compiladores) puede solucionar con el siguiente código:
#include <cxxabi.h>
#include <iostream>
#include <typeinfo>
#include <cstdlib>
namespace some_namespace { namespace another_namespace {
class my_class { };
} }
int main() {
typedef some_namespace::another_namespace::my_class my_type;
// mangled
std::cout << typeid(my_type).name() << std::endl;
// unmangled
int status = 0;
char* demangled = abi::__cxa_demangle(typeid(my_type).name(), 0, 0, &status);
switch (status) {
case -1: {
// could not allocate memory
std::cout << "Could not allocate memory" << std::endl;
return -1;
} break;
case -2: {
// invalid name under the C++ ABI mangling rules
std::cout << "Invalid name" << std::endl;
return -1;
} break;
case -3: {
// invalid argument
std::cout << "Invalid argument to demangle()" << std::endl;
return -1;
} break;
}
std::cout << demangled << std::endl;
free(demangled);
return 0;
}
Muy útil; ¡Gracias! – hauzer
me gusta el método de Nick, una forma completa podría ser esto (para todos los tipos de datos básicos):
template <typename T> const char* typeof(T&) { return "unknown"; } // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(short&) { return "short"; }
template<> const char* typeof(long&) { return "long"; }
template<> const char* typeof(unsigned&) { return "unsigned"; }
template<> const char* typeof(unsigned short&) { return "unsigned short"; }
template<> const char* typeof(unsigned long&) { return "unsigned long"; }
template<> const char* typeof(float&) { return "float"; }
template<> const char* typeof(double&) { return "double"; }
template<> const char* typeof(long double&) { return "long double"; }
template<> const char* typeof(std::string&) { return "String"; }
template<> const char* typeof(char&) { return "char"; }
template<> const char* typeof(signed char&) { return "signed char"; }
template<> const char* typeof(unsigned char&) { return "unsigned char"; }
template<> const char* typeof(char*&) { return "char*"; }
template<> const char* typeof(signed char*&) { return "signed char*"; }
template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }
(i) no funcionará para otros tipos (es decir, no es genérico); (ii) hinchazón de código inútil; (iii) lo mismo puede hacerse (correctamente) con 'typeid' o' decltype'. – edmz
Tiene razón, pero cubre todos los tipos básicos ... y eso es lo que necesito en este momento ... – Jahid
¿Puede decirme cómo lo haría con decltype, – Jahid
Una solución más genérica y sin la sobrecarga de funciones que mi anterior:
template<typename T>
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same<T,int>::value) Type="int";
if(std::is_same<T,std::string>::value) Type="String";
if(std::is_same<T,MyClass>::value) Type="MyClass";
return Type;}
Aquí MyClass es una clase definida por el usuario. Aquí también se pueden agregar más condiciones.
Ejemplo:
#include <iostream>
class MyClass{};
template<typename T>
std::string TypeOf(T){
std::string Type="unknown";
if(std::is_same<T,int>::value) Type="int";
if(std::is_same<T,std::string>::value) Type="String";
if(std::is_same<T,MyClass>::value) Type="MyClass";
return Type;}
int main(){;
int a=0;
std::string s="";
MyClass my;
std::cout<<TypeOf(a)<<std::endl;
std::cout<<TypeOf(s)<<std::endl;
std::cout<<TypeOf(my)<<std::endl;
return 0;}
de salida:
int
String
MyClass
También puede utilizar C++ filt con opción -t (tipo) para demangle el nombre del tipo:
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
int main() {
auto x = 1;
string my_type = typeid(x).name();
system(("echo " + my_type + " | c++filt -t").c_str());
return 0;
}
Probado solo en Linux.
Infierno feo, pero hará lo que necesito. Y mucho más pequeño que las otras soluciones. Funciona en Mac por cierto. –
En C++ 11, tenemos decltype. No es posible en el estándar C++ mostrar el tipo exacto de variable declarada usando decltype. Podemos usar boost typeindex, es decir, type_id_with_cvr
(cvr significa const, volátil, referencia) para imprimir tipo como a continuación.
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int main() {
int i = 0;
const int ci = 0;
cout << "decltype(i) is " << type_id_with_cvr<decltype(i)>().pretty_name() << '\n';
cout << "decltype((i)) is " << type_id_with_cvr<decltype((i))>().pretty_name() << '\n';
cout << "decltype(ci) is " << type_id_with_cvr<decltype(ci)>().pretty_name() << '\n';
cout << "decltype((ci)) is " << type_id_with_cvr<decltype((ci))>().pretty_name() << '\n';
cout << "decltype(std::move(i)) is " << type_id_with_cvr<decltype(std::move(i))>().pretty_name() << '\n';
cout << "decltype(std::static_cast<int&&>(i)) is " << type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name() << '\n';
return 0;
}
sería más simple usar una función de ayuda: 'plantilla
#include <iostream>
#include <typeinfo>
using namespace std;
#define show_type_name(_t) \
system(("echo " + string(typeid(_t).name()) + " | c++filt -t").c_str())
int main() {
auto a = {"one", "two", "three"};
cout << "Type of a: " << typeid(a).name() << endl;
cout << "Real type of a:\n";
show_type_name(a);
for (auto s : a) {
if (string(s) == "one") {
cout << "Type of s: " << typeid(s).name() << endl;
cout << "Real type of s:\n";
show_type_name(s);
}
cout << s << endl;
}
int i = 5;
cout << "Type of i: " << typeid(i).name() << endl;
cout << "Real type of i:\n";
show_type_name(i);
return 0;
}
Salida:
Type of a: St16initializer_listIPKcE
Real type of a:
std::initializer_list<char const*>
Type of s: PKc
Real type of s:
char const*
one
two
three
Type of i: i
Real type of i:
int
Como Yo reto decidí probar qué tan lejos se puede llegar con una plataforma independiente (con suerte) engaño plantilla.
Los nombres se ensamblan por completo en tiempo de compilación. (Lo que significa que typeid(T).name()
no se pudo usar, por lo tanto, debe proporcionar explícitamente nombres para tipos no compuestos. De lo contrario, se mostrarán marcadores de posición.)
Ejemplo:
TYPE_NAME(int)
TYPE_NAME(void)
// You probably should list all primitive types here.
TYPE_NAME(std::string)
int main()
{
// A simple case
std::cout << type_name<void(*)(int)> << '\n';
// -> `void (*)(int)`
// Ugly mess case
// Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
std::cout << type_name<void (std::string::*(int[3],const int, void (*)(std::string)))(volatile int*const*)> << '\n';
// -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`
// A case with undefined types
// If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
std::cout << type_name<std::ostream (*)(int, short)> << '\n';
// -> `class? (*)(int,??)`
// With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.
}
Código:
#include <type_traits>
#include <utility>
static constexpr std::size_t max_str_lit_len = 256;
template <std::size_t I, std::size_t N> constexpr char sl_at(const char (&str)[N])
{
if constexpr(I < N)
return str[I];
else
return '\0';
}
constexpr std::size_t sl_len(const char *str)
{
for (std::size_t i = 0; i < max_str_lit_len; i++)
if (str[i] == '\0')
return i;
return 0;
}
template <char ...C> struct str_lit
{
static constexpr char value[] {C..., '\0'};
static constexpr int size = sl_len(value);
template <typename F, typename ...P> struct concat_impl {using type = typename concat_impl<F>::type::template concat_impl<P...>::type;};
template <char ...CC> struct concat_impl<str_lit<CC...>> {using type = str_lit<C..., CC...>;};
template <typename ...P> using concat = typename concat_impl<P...>::type;
};
template <typename, const char *> struct trim_str_lit_impl;
template <std::size_t ...I, const char *S> struct trim_str_lit_impl<std::index_sequence<I...>, S>
{
using type = str_lit<S[I]...>;
};
template <std::size_t N, const char *S> using trim_str_lit = typename trim_str_lit_impl<std::make_index_sequence<N>, S>::type;
#define STR_LIT(str) ::trim_str_lit<::sl_len(str), ::str_lit<STR_TO_VA(str)>::value>
#define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)
#define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)
#define STR_TO_VA_4(str,off) ::sl_at<off+0>(str),::sl_at<off+1>(str),::sl_at<off+2>(str),::sl_at<off+3>(str)
template <char ...C> constexpr str_lit<C...> make_str_lit(str_lit<C...>) {return {};}
template <std::size_t N> constexpr auto make_str_lit(const char (&str)[N])
{
return trim_str_lit<sl_len((const char (&)[N])str), str>{};
}
template <std::size_t A, std::size_t B> struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};
template <std::size_t A> struct cexpr_pow<A,0> {static constexpr std::size_t value = 1;};
template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_str_lit_impl;
template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_str_lit_impl<N, X, std::index_sequence<Seq...>>
{
static constexpr auto func()
{
if constexpr (N >= cexpr_pow<10,X>::value)
return num_to_str_lit_impl<N, X+1>::func();
else
return str_lit<(N/cexpr_pow<10,X-1-Seq>::value % 10 + '0')...>{};
}
};
template <std::size_t N> using num_to_str_lit = decltype(num_to_str_lit_impl<N,1>::func());
using spa = str_lit<' '>;
using lpa = str_lit<'('>;
using rpa = str_lit<')'>;
using lbr = str_lit<'['>;
using rbr = str_lit<']'>;
using ast = str_lit<'*'>;
using amp = str_lit<'&'>;
using con = str_lit<'c','o','n','s','t'>;
using vol = str_lit<'v','o','l','a','t','i','l','e'>;
using con_vol = con::concat<spa, vol>;
using nsp = str_lit<':',':'>;
using com = str_lit<','>;
using unk = str_lit<'?','?'>;
using c_cla = str_lit<'c','l','a','s','s','?'>;
using c_uni = str_lit<'u','n','i','o','n','?'>;
using c_enu = str_lit<'e','n','u','m','?'>;
template <typename T> inline constexpr bool ptr_or_ref = std::is_pointer_v<T> || std::is_reference_v<T> || std::is_member_pointer_v<T>;
template <typename T> inline constexpr bool func_or_arr = std::is_function_v<T> || std::is_array_v<T>;
template <typename T> struct primitive_type_name {using value = unk;};
template <typename T, typename = std::enable_if_t<std::is_class_v<T>>> using enable_if_class = T;
template <typename T, typename = std::enable_if_t<std::is_union_v<T>>> using enable_if_union = T;
template <typename T, typename = std::enable_if_t<std::is_enum_v <T>>> using enable_if_enum = T;
template <typename T> struct primitive_type_name<enable_if_class<T>> {using value = c_cla;};
template <typename T> struct primitive_type_name<enable_if_union<T>> {using value = c_uni;};
template <typename T> struct primitive_type_name<enable_if_enum <T>> {using value = c_enu;};
template <typename T> struct type_name_impl;
template <typename T> using type_name_lit = std::conditional_t<std::is_same_v<typename primitive_type_name<T>::value::template concat<spa>,
typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>,
typename primitive_type_name<T>::value,
typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>;
template <typename T> inline constexpr const char *type_name = type_name_lit<T>::value;
template <typename T, typename = std::enable_if_t<!std::is_const_v<T> && !std::is_volatile_v<T>>> using enable_if_no_cv = T;
template <typename T> struct type_name_impl
{
using l = typename primitive_type_name<T>::value::template concat<spa>;
using r = str_lit<>;
};
template <typename T> struct type_name_impl<const T>
{
using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,
typename type_name_impl<T>::l>;
using l = std::conditional_t<ptr_or_ref<T>,
typename new_T_l::template concat<con>,
con::concat<new_T_l>>;
using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<volatile T>
{
using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,
typename type_name_impl<T>::l>;
using l = std::conditional_t<ptr_or_ref<T>,
typename new_T_l::template concat<vol>,
vol::concat<new_T_l>>;
using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<const volatile T>
{
using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,
typename type_name_impl<T>::l>;
using l = std::conditional_t<ptr_or_ref<T>,
typename new_T_l::template concat<con_vol>,
con_vol::concat<new_T_l>>;
using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<T *>
{
using l = std::conditional_t<func_or_arr<T>,
typename type_name_impl<T>::l::template concat<lpa, ast>,
typename type_name_impl<T>::l::template concat< ast>>;
using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,
typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &>
{
using l = std::conditional_t<func_or_arr<T>,
typename type_name_impl<T>::l::template concat<lpa, amp>,
typename type_name_impl<T>::l::template concat< amp>>;
using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,
typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &&>
{
using l = std::conditional_t<func_or_arr<T>,
typename type_name_impl<T>::l::template concat<lpa, amp, amp>,
typename type_name_impl<T>::l::template concat< amp, amp>>;
using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,
typename type_name_impl<T>::r>;
};
template <typename T, typename C> struct type_name_impl<T C::*>
{
using l = std::conditional_t<func_or_arr<T>,
typename type_name_impl<T>::l::template concat<lpa, type_name_lit<C>, nsp, ast>,
typename type_name_impl<T>::l::template concat< type_name_lit<C>, nsp, ast>>;
using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,
typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<enable_if_no_cv<T[]>>
{
using l = typename type_name_impl<T>::l;
using r = lbr::concat<rbr, typename type_name_impl<T>::r>;
};
template <typename T, std::size_t N> struct type_name_impl<enable_if_no_cv<T[N]>>
{
using l = typename type_name_impl<T>::l;
using r = lbr::concat<num_to_str_lit<N>, rbr, typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T()>
{
using l = typename type_name_impl<T>::l;
using r = lpa::concat<rpa, typename type_name_impl<T>::r>;
};
template <typename T, typename P1, typename ...P> struct type_name_impl<T(P1, P...)>
{
using l = typename type_name_impl<T>::l;
using r = lpa::concat<type_name_lit<P1>,
com::concat<type_name_lit<P>>..., rpa, typename type_name_impl<T>::r>;
};
#define TYPE_NAME(t) template <> struct primitive_type_name<t> {using value = STR_LIT(#t);};
Va a reconsiderar su opción de respuesta aceptada? Si bien es 5 años más tarde que la respuesta original que usted aceptó, las cosas han cambiado, y creo que la respuesta de @ HowardHinnant es la mejor en estos días. – einpoklum
Aquí hay un resumen de la solución de forma larga de Howard pero implementada con una macro herética de una línea: '#define DEMANGLE_TYPEID_NAME (x) abi :: __ cxa_demangle (typeid ((x)). Name(), NULL, NULL, NULL)' . Si necesita soporte multiplataforma: use '# ifdef',' # else', '# endif' para proporcionar macros para otras plataformas como MSVC. –
Con un requisito de lectura humana más explícito: http://stackoverflow.com/questions/12877521/human-readable-type-info-name –