A continuación se presenta un resumen de la solución de la forma larga de Howard, pero implementado con una macro de una línea herético: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Si necesita soporte multiplataforma: Uso #ifdef, #else, #endifpara proporcionar uno macros para otras plataformas como MSVC.
Si solo usa esto para la depuración, puede considerar template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Luego, por ejemplo, print_T<const int * const **>();se imprimirá void print_T() [T = const int *const **]en tiempo de ejecución y conserva todos los calificadores (funciona en GCC y Clang).
Henri Menke
@Henri, __PRETTY_FUNCTION__no es C ++ estándar (el requisito está en el título de la pregunta).
Toby Speight
Respuestas:
505
Actualización de C ++ 11 a una pregunta muy antigua: escriba el tipo de variable en C ++.
La respuesta aceptada (y buena) es usar typeid(a).name(), donde aes 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 de diferentes tipos (y por razones buenas y comprensibles una vez que esas razones estén expuestas)
¿ typeid(a).name()Nos ayudará nuestro confiable a explorar este mundo nuevo y valiente?
No.
Pero la herramienta que lo hará no es tan complicada. Y es esa herramienta la que estoy usando como respuesta a esta pregunta. Compararé y contrastaré esta nueva herramienta typeid(a).name(). Y esta nueva herramienta en realidad está construida encima typeid(a).name().
La cuestión fundamental:
typeid(a).name()
tira los calificadores cv, referencias y lvalue / rvalue-ness. Por ejemplo:
constint ci =0;
std::cout <<typeid(ci).name()<<'\n';
Para mí salidas:
i
y supongo que en las salidas de MSVC:
int
Es decir, constse ha ido. Este no es un problema de QOI (calidad de implementación). El estándar exige este comportamiento.
Lo que recomiendo a continuación es:
template<typename T> std::string type_name();
que se usaría así:
constint ci =0;
std::cout << type_name<decltype(ci)>()<<'\n';
y para mí salidas:
intconst
<disclaimer>No he probado esto en MSVC. </disclaimer> Pero agradezco los comentarios de quienes lo hacen.
La solución C ++ 11
Estoy usando __cxa_demanglepara plataformas que no son de MSVC como lo recomienda ipapadop en su respuesta a los tipos de demanda. Pero en MSVC estoy confiando typeiden exigir nombres (sin probar). Y este núcleo está envuelto en algunas pruebas simples que detectan, restauran e informan calificadores cv 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(){typedeftypename 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),#elsenullptr,#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 +="&";elseif(std::is_rvalue_reference<T>::value)
r +="&&";return r;}
Los resultados
Con esta solución puedo hacer esto:
int& foo_lref();int&& foo_rref();int foo_value();int
main(){int i =0;constint 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 intdecltype((i)) is int&decltype(ci) is intconstdecltype((ci)) is intconst&decltype(static_cast<int&>(i)) is int&decltype(static_cast<int&&>(i)) is int&&decltype(static_cast<int>(i)) is intdecltype(foo_lref()) is int&decltype(foo_rref()) is int&&decltype(foo_value()) is int
Tenga en cuenta (por ejemplo) la diferencia entre decltype(i)y decltype((i)). El primero es el tipo de declaración de i. Este último es el "tipo" de la expresióni . (las expresiones nunca tienen tipo de referencia, pero como convención decltyperepresenta expresiones de valor con referencias de valor).
Por lo tanto, esta herramienta es un excelente vehículo para aprender decltype, además de explorar y depurar su propio código.
Por el contrario, si tuviera que construir esto solo typeid(a).name(), sin volver a agregar referencias o calificadores cv perdidos, el resultado sería:
decltype(i) is intdecltype((i)) is intdecltype(ci) is intdecltype((ci)) is intdecltype(static_cast<int&>(i)) is intdecltype(static_cast<int&&>(i)) is intdecltype(static_cast<int>(i)) is intdecltype(foo_lref()) is intdecltype(foo_rref()) is intdecltype(foo_value()) is int
Es decir, se eliminan todas las referencias y calificadores cv.
Actualización de C ++ 14
Justo cuando crees que tienes una solución a un problema, alguien siempre sale de la nada y te muestra una forma mucho mejor. :-)
Esta respuesta 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:
¡Está en tiempo de compilación!
Obtiene el compilador para hacer el trabajo en lugar de una biblioteca (incluso un std :: lib). Esto significa resultados más precisos para las últimas características del lenguaje (como lambdas).
La respuesta de Jamboree no muestra todo para VS, y estoy modificando un poco su código. Pero dado que esta respuesta tiene muchas opiniones, tómese un tiempo para ir allí y votar su respuesta, sin lo cual, esta actualización nunca habría sucedido.
Este código se desactivará automáticamente constexprsi todavía está atascado en C ++ 11 antiguo. Y si está pintando en la pared de la cueva con C ++ 98/03, también noexceptse sacrifica.
Actualización de C ++ 17
En los comentarios a continuación, Lyberta señala que lo nuevo std::string_viewpuede reemplazar static_string:
template<class T>constexpr
std::string_view
type_name(){usingnamespace 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 < 201402return string_view(p.data()+36, p.size()-36-1);# elsereturn 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 al muy buen trabajo de detective de Jive Dadson en los comentarios a continuación.
VS 14 CTP imprimió los tipos correctos, solo tuve que agregar una #include <iostream>línea.
Max Galkin
3
¿Por qué plantilla <typename T> std :: string type_name ()? ¿Por qué no pasas un tipo como argumento?
moonman239
2
Creo que mi lógica era que a veces solo tenía un tipo (como un parámetro de plantilla deducido), y no quería tener que construir artificialmente uno de esos para obtener el tipo (aunque en estos días declvalharía el trabajo).
Howard Hinnant
55
@AngelusMortis: Debido a que el inglés es vago / ambiguo en comparación con el código C ++, le animo a que copie / pegue esto en su caso de prueba con el tipo específico que le interesa y con el compilador específico que le interesa, y escriba con más detalles si el resultado es sorprendente y / o insatisfactorio.
Howard Hinnant
3
@HowardHinnant puedes usar en std::string_viewlugar de static_string?
Es posible que deba activar RTTI en las opciones de su compilador para que esto funcione. Además, el resultado de esto depende del compilador. Puede ser un nombre de tipo sin formato o un símbolo de cambio de nombre o algo intermedio.
¿Por qué la cadena devuelta por la función name () está definida como implementación?
Destructor
44
@PravasiMeet No hay una buena razón, que yo sepa. El comité simplemente no quería forzar a los implementadores del compilador a direcciones técnicas particulares, probablemente en retrospectiva.
Konrad Rudolph
2
¿Hay alguna bandera que pueda usar para habilitar RTTI? Tal vez podrías hacer que tu respuesta sea inclusiva.
Jim
44
@Destructor Proporcionar un formato estandarizado de cambio de nombre puede dar la impresión de que la interoperabilidad entre los archivos binarios creados por dos compiladores diferentes es posible y / o segura, cuando no lo es. Debido a que C ++ no tiene un ABI estándar, un esquema de cambio de nombre estándar sería inútil y potencialmente engañoso y peligroso.
Elkvis
1
@ Jim La sección sobre los indicadores del compilador sería un orden de magnitud más largo que la respuesta misma. GCC lo compila de manera predeterminada, por lo tanto, "-fno-rtti", otros compiladores pueden elegir no hacerlo, pero no hay un estándar para los indicadores del compilador.
kfsone
82
Muy feo, pero funciona si solo quieres información de tiempo de compilación (por ejemplo, para depurar):
auto testVar = std::make_tuple(1,1.0,"abc");decltype(testVar)::foo=1;
Devoluciones:
Compilation finished with errors:
source.cpp:In function 'int main()':
source.cpp:5:19: error:'foo' 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 ++.
Karl Pickett
3
@KarlP, bueno, para ser justos, es un poco complicado, esto también funciona :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV
En VC ++ 17, esto reduce una referencia de valor r a una referencia simple, incluso en una función de plantilla con parámetro de referencia de reenvío, y el nombre del objeto envuelto en std :: forward.
Jive Dadson el
¡Pudiste llegar al tipo sin crear ruedas nuevas!
Steven Eckhoff
1
Esta técnica también se describe en "Elemento 4: Sepa cómo ver los tipos deducidos" en C ++ moderno efectivo
lenkite
54
No olvides incluir <typeinfo>
Creo que a lo que se refiere es a la identificación del tipo de tiempo de ejecución. Puedes lograr lo anterior haciendo.
Por lo tanto, no puede usar esta información para la serialización. Pero aún así, la propiedad typeid (a) .name () todavía se puede usar para fines de registro / depuración
¿No imprimirá "int" para cortos y caracteres? ¿Y "flotar" para dobles?
gartenriese
1
La especialización de @gartenriese no tiene ese inconveniente. Porque doublecompilarí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: cpp.sh/2wzc
chappjc
1
@chappjc: Sinceramente, no sé por qué pregunté eso en ese momento, ahora me queda bastante claro. ¡Pero gracias por responder una pregunta de un año de todos modos!
gartenriese
2
@gartenriese me lo imaginé, pero "internet" podría tener la misma pregunta en algún momento.
chappjc
18
Como se mencionó, typeid().name()puede devolver un nombre destrozado. En GCC (y algunos otros compiladores) puede solucionarlo 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;// unmangledint 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);return0;
La DECLARE_TYPE_NAMEdefinición existe para facilitarle la vida al declarar esta clase de rasgos para todos los tipos que espera necesitar.
Esto podría ser más útil que las soluciones involucradas typeidporque puedes controlar la salida. Por ejemplo, usar typeidfor long longen mi compilador da "x".
En C ++ 11, tenemos decltype. No hay forma en c ++ estándar para 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 el tipo como se muestra a continuación.
#include<iostream>#include<boost/type_index.hpp>usingnamespace std;using boost::typeindex::type_id_with_cvr;int main(){int i =0;constint 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';return0;}
sería más simple usar una función auxiliar:template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
r0ng
6
También puede usar c ++ filt con la opción -t (tipo) para solicitar el nombre del tipo:
Infierno feo pero hará lo que necesito. Y mucho más pequeño que las otras soluciones. Funciona en Mac por cierto.
Marco Luglio
6
Howard Hinnant usó números mágicos para extraer el nombre del tipo. 康 桓 瑋 sugirió el prefijo y sufijo de la cadena. Pero el prefijo / sufijo sigue cambiando. Con "probe_type" type_name calcula automáticamente los tamaños de prefijo y sufijo para "probe_type" para extraer el nombre del tipo:
test
constint*&unsignedintconstintconstint*constint*&constexpr std::string_view type_name()[with T = probe_type; std::string_view = std::basic_string_view<char>]
clang 10.0.0 Wandbox:
test
constint*&unsignedintconstintconstint*constint*&
std::__1::string_view type_name()[T = probe_type]
VS 2019 versión 16.3.3:
class test
constint*&unsignedintconstintconstint*constint*&class std::basic_string_view<char,struct std::char_traits<char>> __cdecl type_name<class probe_type>(void)
Las otras respuestas que involucran RTTI (typeid) son probablemente lo que desea, siempre que:
puede permitirse la sobrecarga de memoria (que puede ser considerable con algunos compiladores)
los nombres de clase que devuelve su compilador son útiles
La alternativa, (similar a la respuesta de Greg Hewgill), es construir una tabla de rasgos en tiempo de compilación.
template<typename T>struct type_as_string;// declare your Wibble type (probably with definition of Wibble)template<>struct type_as_string<Wibble>{staticconstchar*const value ="Wibble";};
Tenga en cuenta que si ajusta las declaraciones en una macro, tendrá problemas para declarar nombres para los tipos de plantilla que toman 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
(i) no funcionará para otros tipos (es decir, no es genérico en absoluto); (ii) hinchazón de código inútil; (iii) lo mismo puede hacerse (correctamente) con typeido decltype.
edmz
2
Tienes razón, pero cubre todos los tipos básicos ... y eso es lo que necesito ahora ...
Jahid
2
¿Puedes decirme cómo lo harías con decltype?
Jahid
1
Si es una prueba en tiempo de compilación, puede usar std :: is_same <T, S> y decltype para obtener T y S.
edmz
4
Mientras desafiaba decidí probar hasta dónde se podía llegar con un truco de plantilla independiente de la plataforma (con suerte).
Los nombres se ensamblan completamente en el momento de la compilación. (Lo que significa typeid(T).name()que no se puede utilizar, por lo que debe proporcionar explícitamente nombres para tipos no compuestos. De lo contrario, se mostrarán marcadores de posición).
Ejemplo de uso:
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],constint,void(*)(std::string)))(volatileint*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>staticconstexpr std::size_t max_str_lit_len =256;template<std::size_t I, std::size_t N>constexprchar sl_at(constchar(&str)[N]){ifconstexpr(I < N)return str[I];elsereturn'\0';}constexpr std::size_t sl_len(constchar*str){for(std::size_t i =0; i < max_str_lit_len; i++)if(str[i]=='\0')return i;return0;}template<char...C>struct str_lit
{staticconstexprchar value[]{C...,'\0'};staticconstexprint 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,constchar*>struct trim_str_lit_impl;template<std::size_t...I,constchar*S>struct trim_str_lit_impl<std::index_sequence<I...>, S>{using type = str_lit<S[I]...>;};template<std::size_t N,constchar*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>constexprauto make_str_lit(constchar(&str)[N]){return trim_str_lit<sl_len((constchar(&)[N])str), str>{};}template<std::size_t A, std::size_t B>struct cexpr_pow {staticconstexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};template<std::size_t A>struct cexpr_pow<A,0>{staticconstexpr 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...>>{staticconstexprauto func(){ifconstexpr(N >= cexpr_pow<10,X>::value)return num_to_str_lit_impl<N, X+1>::func();elsereturn 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>inlineconstexprbool ptr_or_ref = std::is_pointer_v<T>|| std::is_reference_v<T>|| std::is_member_pointer_v<T>;template<typename T>inlineconstexprbool 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>inlineconstexprconstchar*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<constvolatile 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);};
#include<iostream>#include<typeinfo>usingnamespace 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);return0;}
Salida:
Type of a:St16initializer_listIPKcEReal type of a:
std::initializer_list<charconst*>Type of s:PKcReal type of s:charconst*
one
two
three
Type of i: i
Real type of i:int
Como explicó Scott Meyers en Effective Modern C ++,
Las llamadas a std::type_info::nameno están garantizados para volver anythong sensata.
La mejor solución es dejar que el compilador genere un mensaje de error durante la deducción de tipo, por ejemplo,
template<typename T>class TD;int main(){constint theAnswer =32;auto x = theAnswer;auto y =&theAnswer;
TD<decltype(x)> xType;
TD<decltype(y)> yType;return0;}
El resultado será algo como esto, dependiendo de los compiladores,
test4.cpp:10:21: error: aggregate ‘TD<int> xType’ has incomplete type and cannot be defined TD<decltype(x)> xType;
test4.cpp:11:21: error: aggregate ‘TD<constint*> yType’ has incomplete type and cannot be defined TD<decltype(y)> yType;
Por lo tanto, llegamos a saber que xel tipo es int, yel tipo esconst int*
Para cualquiera que todavía visite, recientemente tuve el mismo problema y decidí escribir una pequeña biblioteca basada en las respuestas de esta publicación. Proporciona nombres de tipo constexpr e índices de tipo y se prueba en Mac, Windows y Ubuntu.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Si necesita soporte multiplataforma: Uso#ifdef
,#else
,#endif
para proporcionar uno macros para otras plataformas como MSVC.template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Luego, por ejemplo,print_T<const int * const **>();
se imprimirávoid print_T() [T = const int *const **]
en tiempo de ejecución y conserva todos los calificadores (funciona en GCC y Clang).__PRETTY_FUNCTION__
no es C ++ estándar (el requisito está en el título de la pregunta).Respuestas:
Actualización de C ++ 11 a una pregunta muy antigua: escriba el tipo de variable en C ++.
La respuesta aceptada (y buena) es usar
typeid(a).name()
, dondea
es un nombre de variable.Ahora en C ++ 11 tenemos
decltype(x)
, que puede convertir una expresión en un tipo. Ydecltype()
viene con su propio conjunto de reglas muy interesantes. Por ejemplo,decltype(a)
ydecltype((a))
generalmente serán de diferentes tipos (y por razones buenas y comprensibles una vez que esas razones estén expuestas)¿
typeid(a).name()
Nos ayudará nuestro confiable a explorar este mundo nuevo y valiente?No.
Pero la herramienta que lo hará no es tan complicada. Y es esa herramienta la que estoy usando como respuesta a esta pregunta. Compararé y contrastaré esta nueva herramienta
typeid(a).name()
. Y esta nueva herramienta en realidad está construida encimatypeid(a).name()
.La cuestión fundamental:
tira los calificadores cv, referencias y lvalue / rvalue-ness. Por ejemplo:
Para mí salidas:
y supongo que en las salidas de MSVC:
Es decir,
const
se ha ido. Este no es un problema de QOI (calidad de implementación). El estándar exige este comportamiento.Lo que recomiendo a continuación es:
que se usaría así:
y para mí salidas:
<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 lo recomienda ipapadop en su respuesta a los tipos de demanda. Pero en MSVC estoy confiandotypeid
en exigir nombres (sin probar). Y este núcleo está envuelto en algunas pruebas simples que detectan, restauran e informan calificadores cv y referencias al tipo de entrada.Los resultados
Con esta solución puedo hacer esto:
y la salida es:
Tenga en cuenta (por ejemplo) la diferencia entre
decltype(i)
ydecltype((i))
. El primero es el tipo de declaración dei
. Este último es el "tipo" de la expresióni
. (las expresiones nunca tienen tipo de referencia, pero como convencióndecltype
representa expresiones de valor con referencias de valor).Por lo tanto, esta herramienta es un excelente vehículo para aprender
decltype
, además de explorar y depurar su propio código.Por el contrario, si tuviera que construir esto solo
typeid(a).name()
, sin volver a agregar referencias o calificadores cv perdidos, el resultado sería:Es decir, se eliminan todas las referencias y calificadores cv.
Actualización de C ++ 14
Justo cuando crees que tienes una solución a un problema, alguien siempre sale de la nada y te muestra una forma mucho mejor. :-)
Esta respuesta 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:
La respuesta de Jamboree no muestra todo para VS, y estoy modificando un poco su código. Pero dado que esta respuesta tiene muchas opiniones, tómese un tiempo para ir allí y votar su respuesta, sin lo cual, esta actualización nunca habría sucedido.
Este código se desactivará automáticamente
constexpr
si todavía está atascado en C ++ 11 antiguo. Y si está pintando en la pared de la cueva con C ++ 98/03, tambiénnoexcept
se sacrifica.Actualización de C ++ 17
En los comentarios a continuación, Lyberta señala que lo nuevo
std::string_view
puede reemplazarstatic_string
:He actualizado las constantes para VS gracias al muy buen trabajo de detective de Jive Dadson en los comentarios a continuación.
Actualizar:
Asegúrese de revisar esta reescritura a continuación, que elimina los números mágicos ilegibles en mi última formulación.
fuente
#include <iostream>
línea.declval
haría el trabajo).std::string_view
lugar destatic_string
?Tratar:
Es posible que deba activar RTTI en las opciones de su compilador para que esto funcione. Además, el resultado de esto depende del compilador. Puede ser un nombre de tipo sin formato o un símbolo de cambio de nombre o algo intermedio.
fuente
Muy feo, pero funciona si solo quieres información de tiempo de compilación (por ejemplo, para depurar):
Devoluciones:
fuente
auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
No olvides incluir
<typeinfo>
Creo que a lo que se refiere es a la identificación del tipo de tiempo de ejecución. Puedes lograr lo anterior haciendo.
fuente
Según la solución de Howard , si no quieres el número mágico, creo que esta es una buena manera de representar y luce intuitivo:
fuente
Tenga en cuenta que los nombres generados por la función RTTI de C ++ no son portables. Por ejemplo, la clase
tendrá los siguientes nombres:
Por lo tanto, no puede usar esta información para la serialización. Pero aún así, la propiedad typeid (a) .name () todavía se puede usar para fines de registro / depuración
fuente
Puedes usar plantillas.
En el ejemplo anterior, cuando el tipo no coincide, imprimirá "desconocido".
fuente
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: cpp.sh/2wzcComo se mencionó,
typeid().name()
puede devolver un nombre destrozado. En GCC (y algunos otros compiladores) puede solucionarlo con el siguiente código:}
fuente
Podrías usar una clase de rasgos para esto. Algo como:
La
DECLARE_TYPE_NAME
definición existe para facilitarle la vida al declarar esta clase de rasgos para todos los tipos que espera necesitar.Esto podría ser más útil que las soluciones involucradas
typeid
porque puedes controlar la salida. Por ejemplo, usartypeid
forlong long
en mi compilador da "x".fuente
En C ++ 11, tenemos decltype. No hay forma en c ++ estándar para 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 el tipo como se muestra a continuación.fuente
template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
También puede usar c ++ filt con la opción -t (tipo) para solicitar el nombre del tipo:
Probado solo en Linux.
fuente
Howard Hinnant usó números mágicos para extraer el nombre del tipo. 康 桓 瑋 sugirió el prefijo y sufijo de la cadena. Pero el prefijo / sufijo sigue cambiando. Con "probe_type" type_name calcula automáticamente los tamaños de prefijo y sufijo para "probe_type" para extraer el nombre del tipo:
Salida
gcc 10.0.0 20190919 Wandbox:
clang 10.0.0 Wandbox:
VS 2019 versión 16.3.3:
fuente
Las otras respuestas que involucran RTTI (typeid) son probablemente lo que desea, siempre que:
La alternativa, (similar a la respuesta de Greg Hewgill), es construir una tabla de rasgos en tiempo de compilación.
Tenga en cuenta que si ajusta las declaraciones en una macro, tendrá problemas para declarar nombres para los tipos de plantilla que toman 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
fuente
Una solución más genérica sin sobrecarga de funciones que la anterior:
Aquí MyClass es una clase definida por el usuario. Aquí también se pueden agregar más condiciones.
Ejemplo:
Salida:
fuente
Me gusta el método de Nick. Un formulario completo podría ser este (para todos los tipos de datos básicos):
fuente
typeid
odecltype
.Mientras desafiaba decidí probar hasta dónde se podía llegar con un truco de plantilla independiente de la plataforma (con suerte).
Los nombres se ensamblan completamente en el momento de la compilación. (Lo que significa
typeid(T).name()
que no se puede utilizar, por lo que debe proporcionar explícitamente nombres para tipos no compuestos. De lo contrario, se mostrarán marcadores de posición).Ejemplo de uso:
Código:
fuente
Salida:
fuente
Como explicó Scott Meyers en Effective Modern C ++,
La mejor solución es dejar que el compilador genere un mensaje de error durante la deducción de tipo, por ejemplo,
El resultado será algo como esto, dependiendo de los compiladores,
Por lo tanto, llegamos a saber que
x
el tipo esint
,y
el tipo esconst int*
fuente
Para cualquiera que todavía visite, recientemente tuve el mismo problema y decidí escribir una pequeña biblioteca basada en las respuestas de esta publicación. Proporciona nombres de tipo constexpr e índices de tipo y se prueba en Mac, Windows y Ubuntu.
El código de la biblioteca está aquí: https://github.com/TheLartians/StaticTypeInfo
fuente