Frage

Ich möchte den string name (const char *) eines Template-Typ erhalten. Leider habe ich keinen Zugriff auf RTTI.

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return /* magic goes here */; }
}

So

SomeClass<int> sc;
sc.GetClassName();   // returns "int"

Ist das möglich? Ich kann nicht einen Weg und bin finden aufgeben. Danke für die Hilfe.

War es hilfreich?

Lösung

Nein, und es wird auch nicht mit typeid zuverlässig arbeiten. Es wird Ihnen einige interne Zeichenfolge, die auf der Compiler Implementierung abhängt. So etwas wie "int", sondern auch "i" ist üblich, int.

Durch die Art und Weise, wenn das, was Sie wollen, ist nur zu vergleichen, ob zwei Typen gleich sind, brauchen Sie nicht, sie in eine Zeichenfolge zu konvertieren zuerst. Sie können nur das tun,

template<typename A, typename B>
struct is_same { enum { value = false }; };

template<typename A>
struct is_same<A, A> { enum { value = true }; };

Und dann tun

if(is_same<T, U>::value) { ... }

-Boost hat bereits eine solche Vorlage, und der nächste C ++ Standards wird haben std::is_same.

Manuelle Registrierung von Typen

Sie können sich auf die Arten wie folgt spezialisiert:

template<typename> 
struct to_string {
    // optionally, add other information, like the size
    // of the string.
    static char const* value() { return "unknown"; }
};

#define DEF_TYPE(X) \
    template<> struct to_string<X> { \
        static char const* value() { return #X; } \
    }

DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...

So können Sie es wie

verwenden
char const *s = to_string<T>::value();

Natürlich können Sie auch die Beseitigung der primären Template-Definition erhalten (und halten nur die Vorwärtsdeklaration), wenn Sie einen Kompilierung-Fehler erhalten möchten, wenn der Typ nicht bekannt ist. Ich schloss es gerade hier für die Fertigstellung.

ich verwendet, um statische Daten-Mitglieder char const * zuvor, aber sie verursachen einige komplizierte Probleme, wie Fragen, bei denen Erklärungen von ihnen zu setzen, und so weiter. Klasse Spezialisierungen wie oben leicht lösen das Problem.

Automatisch, je nach GCC

Ein weiterer Ansatz ist auf Compiler-Interna zu verlassen. In GCC, folgend ich vernünftige Ergebnisse:

template<typename T>
std::string print_T() {
    return __PRETTY_FUNCTION__;
}

für std::string Rückkehr.

  

std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]

Einige substr Magie mit find vermischte geben Ihnen die String-Darstellung die Sie suchen.

Andere Tipps

Die wirklich einfache Lösung: übergeben Sie einfach ein String-Objekt an den Konstruktor von Someclass, der sagt, was der Typ ist.

Beispiel:

#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));

Einfach es speichern und sie bei der Umsetzung von GetClassName angezeigt werden soll.

etwas kompliziertere Lösung, aber immer noch recht einfach:

#define DEC_SOMECLASS(T, name) SomeClass<T> name;  name.sType = #T; 

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return sType.c_str(); }
    std::string sType;
};


int main(int argc, char **argv)
{
    DEC_SOMECLASS(int, s);
    const char *p = s.GetClassName();

    return 0;
}

Vorlage nicht Typ Lösung:

Sie können auch Ihre eigene Art ids machen und eine Funktion haben, zu und von der ID und der Stringdarstellung zu konvertieren.

Dann können Sie die ID übergeben, wenn Sie den Typ als Vorlage Nicht-Typ-Parameter deklarieren:

template< typename T, int TYPEID>
struct SomeClass
{
    const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};


...

SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;

Sie können so etwas wie dies versuchen (Warnung dies direkt an der Spitze von meinem Kopf ist, so kann es sein, Fehler kompilieren, etc ..)

template <typename T>
const char* GetTypeName()
{
    STATIC_ASSERT(0); // Not implemented for this type
}

#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }

// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return GetTypeName<T>; }
}

Dies wird für jede Art arbeiten, dass Sie eine GETTYPENAME(type) Linie für hinzufügen. Es hat den Vorteil, dass es funktioniert, ohne die Arten zu modifizieren Sie interessiert sind, und wird mit eingebauten und Zeigertypen arbeiten. Es hat den entscheidenden Nachteil, dass Sie müssen eine Linie für jede Art Sie verwenden möchten.

Ohne die eingebaute in RTTI zu verwenden, Sie gehen die Informationen selbst irgendwo, entweder Brian R. Bondy Antwort zu haben, hinzufügen oder dirkgently wird arbeiten. Zusammen mit meiner Antwort, haben Sie drei verschiedene Standorte diese Informationen hinzuzufügen:

  1. Bei der Erzeugung der Objekte mit SomeClass<int>("int")
  2. In der Klasse mit dirkgently der Kompilierung-RTTI oder virtuelle Funktionen
  3. Mit der Vorlage meiner Lösung.

Alle drei arbeiten, es ist nur eine Frage davon, wo Sie mit den am wenigsten Wartung Kopfschmerzen in Ihrer Situation am Ende dann.

Mit dem Sie keinen Zugriff auf RTTI haben, das bedeutet Sie nicht typeid (T) .name () verwenden? Denn das ist so ziemlich der einzige Weg, um es mit dem Compiler Hilfe zu tun.

Ist es sehr wichtig, dass die Typen eindeutige Namen zu haben, oder werden die Namen irgendwie beibehalten werden? Wenn ja, sollten Sie überlegen, ihnen etwas robuster als nur den Namen der Klasse als erklärt in dem Code zu geben. Sie können zwei Klassen den gleichen unqualifizierten Namen geben, indem sie in einem anderen Namensraum setzen. Sie können setzten zwei Klassen auch mit dem gleichen Namen (einschließlich Namespace Qualifikation) in zwei verschiedenen DLLs unter Windows, so müssen Sie die von der DLL identifizieren als auch im Namen enthalten sein.

Es hängt alles davon ab, was Sie vorhaben, mit den Saiten zu tun, natürlich.

Sie können ein wenig Magie selbst hinzufügen. So etwas wie:

#include <iostream>

#define str(x) #x
#define xstr(x) str(x)
#define make_pre(C) concat(C, <)
#define make_post(t) concat(t, >)

#define make_type(C, T) make_pre(C) ## make_post(T)
#define CTTI_REFLECTION(T, x)  static std::string my_typeid() \
                               { return xstr(make_type(T, x)); }


// the dark magic of Compile Time Type Information (TM)
#define CTTI_REFLECTION(x)  static const char * my_typeid() \
                                  { return xstr(make_type(T, x)); }

#define CREATE_TEMPLATE(class_name, type) template<> \
                                    struct class_name <type>{ \
                                        CTTI_REFLECTION(class_name, type) \
                                    }; 

// dummy, we'll specialize from this later
template<typename T> struct test_reflection;

// create an actual class
CREATE_TEMPLATE(test_reflection, int)

struct test_reflection {
  CTTI_REFLECTION(test_reflection)
};

int main(int argc, char* argv[])
{
    std::cout << test_reflection<int>::my_typeid();
}

Ich werde den Inspektoren eine static Funktion machen (und damit nicht-const).

Nein, leider nicht.

Und RTTI wird nicht einmal kompilieren, wenn Sie versuchen, es auf int zu verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top