Zum Beispiel:
int a = 12;
cout << typeof(a) << endl;
Erwartetes Ergebnis:
int
Zum Beispiel:
int a = 12;
cout << typeof(a) << endl;
Erwartetes Ergebnis:
int
Beachten Sie, dass die von der RTTI-Funktion von C++ erzeugten Namen no tragbar. Zum Beispiel kann die Klasse
MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>
werden die folgenden Namen haben:
// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE
Sie können diese Informationen also nicht für die Serialisierung verwenden. Dennoch kann die Eigenschaft typeid(a).name() für Protokoll-/Debug-Zwecke verwendet werden
Wie bereits erwähnt, typeid().name()
kann einen verstümmelten Namen zurückgeben. In GCC (und einigen anderen Compilern) können Sie dies mit dem folgenden Code umgehen:
#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;
}
Sie können Vorlagen verwenden.
template <typename T> const char* typeof(T&) { return "unknown"; } // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }
Im obigen Beispiel wird, wenn der Typ nicht übereinstimmt, "unbekannt" gedruckt.
@gartenriese Spezialisierung hat diesen Nachteil nicht. Für double
würde die nicht spezialisierte Version der Vorlagenfunktion kompiliert, anstatt eine implizite Typkonvertierung durchzuführen, um die Spezialisierung zu verwenden: cpp.sh/2wzc
@chappjc: Ich weiß ehrlich gesagt nicht, warum ich das damals gefragt habe, jetzt ist es mir ziemlich klar. Aber trotzdem danke für die Antwort auf eine ein Jahr alte Frage!
Eine andere Sichtweise auf <a href="https://stackoverflow.com/a/56766138">@'s Antwort </a>(ursprünglich ), wobei weniger Annahmen über die Besonderheiten der Präfixe und Suffixe gemacht werden, und inspiriert von <a href="https://stackoverflow.com/a/58331141/1593077">@Val's Antwort </a>- aber ohne den globalen Namensraum zu verschmutzen; ohne Bedingungen; und hoffentlich leichter zu lesen.
Die gängigen Compiler stellen ein Makro mit der Signatur der aktuellen Funktion bereit. Nun sind Funktionen schablonenfähig; die Signatur enthält also die Schablonenargumente. Der grundlegende Ansatz ist also: Geben Sie einen Typ an, seien Sie in einer Funktion mit diesem Typ als Vorlagenargument.
Leider ist der Typname in einen Text zur Beschreibung der Funktion verpackt, der von Compiler zu Compiler unterschiedlich ist. Mit GCC zum Beispiel ist die Signatur von template <typename T> int foo()
mit Typ double
ist: int foo() [T = double]
.
Wie wird man also den Wrapper-Text los? Die Lösung von @HowardHinnant ist die kürzeste und "direkteste": Verwenden Sie einfach magische Zahlen pro Compiler, um ein Präfix und ein Suffix zu entfernen. Aber das ist natürlich sehr spröde; und niemand mag mag magische Zahlen in seinem Code. Es stellt sich jedoch heraus, dass man, wenn man den Makro-Wert für einen Typ mit bekanntem Namen hat, feststellen kann, welches Präfix und Suffix die Umhüllung bilden.
#include <string_view>
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);
}
Siehe auf GodBolt . Dies sollte auch mit MSVC funktionieren.
Howard Hinnant magische Zahlen verwendet, um den Typnamen zu extrahieren. vorgeschlagenes Präfix und Suffix der Zeichenfolge. Aber Präfix/Suffix ändern sich ständig. Mit "probe_type" berechnet type_name automatisch die Präfix- und Suffixgrößen für "probe_type", um den Typnamen zu extrahieren:
#include <string_view>
using namespace std;
namespace typeName {
template <typename T>
constexpr string_view wrapped_type_name () {
#ifdef __clang__
return __PRETTY_FUNCTION__;
#elif defined(__GNUC__)
return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
return __FUNCSIG__;
#endif
}
class probe_type;
constexpr string_view probe_type_name ("typeName::probe_type");
constexpr string_view probe_type_name_elaborated ("class typeName::probe_type");
constexpr string_view probe_type_name_used (wrapped_type_name<probe_type> ().find (probe_type_name_elaborated) != -1 ? probe_type_name_elaborated : probe_type_name);
constexpr size_t prefix_size () {
return wrapped_type_name<probe_type> ().find (probe_type_name_used);
}
constexpr size_t suffix_size () {
return wrapped_type_name<probe_type> ().length () - prefix_size () - probe_type_name_used.length ();
}
template <typename T>
string_view type_name () {
constexpr auto type_name = wrapped_type_name<T> ();
return type_name.substr (prefix_size (), type_name.length () - prefix_size () - suffix_size ());
}
}
#include <iostream>
using typeName::type_name;
using typeName::probe_type;
class test;
int main () {
cout << type_name<class test> () << endl;
cout << type_name<const int*&> () << endl;
cout << type_name<unsigned int> () << endl;
const int ic = 42;
const int* pic = ⁣
const int*& rpic = pic;
cout << type_name<decltype(ic)> () << endl;
cout << type_name<decltype(pic)> () << endl;
cout << type_name<decltype(rpic)> () << endl;
cout << type_name<probe_type> () << endl;
}
Ausgabe
gcc 10.2 :
test
const int *&
unsigned int
const int
const int *
const int *&
typeName::probe_type
test
const int *&
unsigned int
const int
const int *
const int *&
typeName::probe_type
VS 2019 Version 16.7.6:
class test
const int*&
unsigned int
const int
const int*
const int*&
class typeName::probe_type
Tolle Lösung! Dem class_specifier fehlt jedoch ein Leerzeichen am Ende, so dass die Größe des Präfixes falsch berechnet wird, indem sie ein Zeichen kleiner ist als sie sein sollte. Ich kann den Beitrag nicht selbst bearbeiten, da es sich um eine zu kleine Änderung handelt. Die Compilerausgaben sollten dann entsprechend geändert werden.
Eigentlich machen Sie eine Annahme noch qualitativ anders als @ : Die Annahme, dass für msvc gibt es ein extra Präfix von "class ", und für GCC und clang es nicht existiert. Wenn Präfixe und Suffixe sich ändern können, dann können auch que . Es ist also nicht klar, ob dies besser ist, als eine Annahme über die gesamte Vorwahl zu treffen.
... wie auch immer, würden Sie sich bitte meinen eigenen Lösungsvorschlag unten ansehen? Und mir sagen, ob ich einige zusätzliche Anpassung für MSVC benötigen?
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
3 Stimmen
Hier ist eine Zusammenfassung von Howards Langformlösung, die jedoch mit einem ketzerischen einzeiligen Makro umgesetzt wurde:
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Wenn Sie plattformübergreifende Unterstützung benötigen: Verwenden Sie .#ifdef
,#else
,#endif
um ein Makro für andere Plattformen wie MSVC bereitzustellen.0 Stimmen
Mit expliziteren, für Menschen lesbaren Anforderungen: stackoverflow.com/questions/12877521/
3 Stimmen
Wenn Sie dies nur zum Debuggen verwenden, sollten Sie Folgendes in Betracht ziehen
template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Dann verwenden Sie z.B.print_T<const int * const **>();
wird gedrucktvoid print_T() [T = const int *const **]
zur Laufzeit und bewahrt alle Qualifier (funktioniert in GCC und Clang).0 Stimmen
@Henri,
__PRETTY_FUNCTION__
ist nicht Standard C++ (die Anforderung steht im Titel der Frage).