Domanda

Voglio ottenere il nome della stringa (const char *) di un tipo di modello. Purtroppo non ho accesso alle RTTI.

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

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

È possibile? Non riesco a trovare un modo e sono sul punto di rinunciare. Grazie per l'aiuto.

È stato utile?

Soluzione

No e non funzionerà affidabile con typeid sia. Essa vi darà qualche stringa interna che dipende dall'implementazione del compilatore. Qualcosa come "int", ma anche "i" è comune per int.

A proposito, se quello che vuoi è quello di confrontare solo se due tipi sono gli stessi, non c'è bisogno di convertirli in una stringa prima. Si può solo fare

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

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

E poi fare

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

Boost ha già un tale modello, e la prossima C ++ standard avrà std::is_same troppo.

registrazione manuale dei tipi

È possibile specializzarsi sui tipi come questo:

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); ...

Quindi, è possibile utilizzarlo come

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

Naturalmente, è anche possibile eliminare la definizione del modello primario (e mantenere solo la dichiarazione in avanti) se si vuole ottenere un errore di compilazione se il tipo non è noto. Ho appena incluso qui per il completamento.

Ho usato statici data-membri del const char * in precedenza, ma causare alcuni problemi intricati, come le domande dove mettere le dichiarazioni di loro, e così via. specializzazioni di classe come sopra risolvere il problema facilmente.

automatici, a seconda GCC

Un altro approccio è quello di affidarsi a meccanismi interni del compilatore. Nel GCC, il seguente mi dà risultati ragionevoli:

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

Tornando per std::string.

  

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

Alcuni magia substr mescolati con find vi darà la rappresentazione di stringa che cercate.

Altri suggerimenti

La soluzione davvero semplice: Basta passare un oggetto stringa al costruttore di SomeClass che dice ciò che il tipo è.

Esempio:

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

È sufficiente conservarlo e visualizzarlo nella realizzazione di GetClassName.

soluzione un po 'più complicato, ma ancora abbastanza facile:

#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;
}

modello di soluzione non Tipo:

Si potrebbe anche fare il proprio tipo di ids e hanno una funzione per convertire da e per l'ID e la rappresentazione di stringa.

Poi si può passare l'ID quando si dichiara il tipo come un parametro di modello non-type:

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;

Si può provare qualcosa di simile (avviso questa è solo la parte superiore della mia testa, così ci possono essere errori di compilazione, ecc ..)

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>; }
}

Questo funziona per qualsiasi tipo che si aggiunge una linea GETTYPENAME(type) per. Ha il vantaggio che funziona senza modificare i tipi che vi interessano, e lavorerà con built-in e tipi di puntatore. Esso ha lo svantaggio distinto che è necessario una linea per ogni tipo che si desidera utilizzare.

Senza utilizzare il built-in RTTI, si sta andando ad avere per aggiungere le informazioni da qualche parte, sia la risposta di Brian R. Bondy o dirkgently di funzioneranno. Insieme con la mia risposta, si dispone di tre luoghi diversi per aggiungere queste informazioni:

  1. Al momento della creazione dell'oggetto usando SomeClass<int>("int")
  2. Nella classe utilizzando dirkgently di compilazione tempo RTTI o funzioni virtuali
  3. Con il modello utilizzando la mia soluzione.

Tutti e tre lavoreranno, è solo una questione di dove ci si ritroverà con meno problemi di manutenzione nella vostra situazione.

Per non si ha accesso a RTTI, significa che non è possibile utilizzare typeid (T) .name ()? Perché questo è praticamente l'unico modo per farlo con l'aiuto del compilatore.

E 'molto importante per i tipi di avere nomi univoci, o sono i nomi stanno per essere persistito in qualche modo? Se è così, si dovrebbe considerare dando loro qualcosa di più robusto che solo il nome della classe come dichiarato nel codice. È possibile dare due classi lo stesso nome non qualificato mettendoli in diversi spazi dei nomi. Si può anche mettere due classi con lo stesso nome (tra cui la qualificazione dello spazio dei nomi) in due diverse DLL su Windows, quindi è necessario l'identità della DLL da includere nel nome pure.

Tutto dipende da quello che stai andando a che fare con le corde, naturalmente.

È possibile aggiungere un po 'di magia da soli. Qualcosa di simile:

#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();
}

Farò l'ispettore una funzione static (e quindi non const).

No, mi spiace.

E RTTI non sarà nemmeno la compilazione se si tenta di utilizzarlo su int.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top