Frage

Ich habe einige Template-Klassen schreiben für parseing einige Textdateien, und als solche ist es likly die große Mehrheit der Parse-Fehler werden aufgrund von Fehlern in der Datendatei sein, die zum größten Teil nicht von Programmierern geschrieben, und brauchen so eine nette Nachricht darüber, warum die App fehlgeschlagen zB laden so etwas wie:

  

Fehler bei der Analyse example.txt. Wert ( "notaninteger") von [MySectiom] Key ist keine gültige int

Ich kann die Datei, Abschnitt erarbeiten und Schlüsselnamen von den Argumenten für die Template-Funktion übergeben und Mitglied Vars in der Klasse, aber ich bin nicht sicher, wie den Namen des Typs erhält die Template-Funktion zu konvertieren versucht zu.

Mein aktueller Code wie folgt aussieht, mit Spezialisierungen für nur blanke Saiten und so:

template<typename T> T GetValue(const std::wstring &section, const std::wstring &key)
{
    std::map<std::wstring, std::wstring>::iterator it = map[section].find(key);
    if(it == map[section].end())
        throw ItemDoesNotExist(file, section, key)
    else
    {
        try{return boost::lexical_cast<T>(it->second);}
        //needs to get the name from T somehow
        catch(...)throw ParseError(file, section, key, it->second, TypeName(T));
    }
}

Id eher nicht spezifisch Überlastungen für jede Art machen müssen, dass die Datendateien verwenden können, da gibt es viele von ihnen ...

Auch ich brauche eine Lösung, die keinen Laufzeitaufwand nicht ausgelöst werden, wenn eine Ausnahme auftritt, das heißt, eine vollständig Zeitlösung zusammenstellen werde, was ich will, da dieser Code Tonnen Zeiten und Ladezeiten aufgerufen wird, sind schon etwas lang werden.

EDIT: Ok das ist die Lösung kam ich mit:

Ich habe ein types.h containg die folgende

#pragma once
template<typename T> const wchar_t *GetTypeName();

#define DEFINE_TYPE_NAME(type, name) \
    template<>const wchar_t *GetTypeName<type>(){return name;}

Dann kann ich den DEFINE_TYPE_NAME Makro in CPP-Dateien verwende ich für jede Art muß mit (zB in der CPP-Datei, die die Art, mit zu beginnen definiert) zu behandeln.

Der Linker ist dann in der Lage, die appropirate Template-Spezialisierung, so lange zu finden, da es irgendwo definiert wurde, oder einen Linker Fehler sonst werfen, so dass ich den Typen hinzufügen kann.

War es hilfreich?

Lösung

Jesse Beder Lösung ist wahrscheinlich die beste, aber wenn man die Namen nicht mögen liefert typeid Sie gibt (ich glaube, gcc Sie verstümmelten Namen zum Beispiel gibt), können Sie so etwas wie tun:

template<typename T>
struct TypeParseTraits;

#define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \
    { static const char* name; } ; const char* TypeParseTraits<X>::name = #X


REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

Und verwenden Sie es dann wie

throw ParseError(TypeParseTraits<T>::name);

EDIT:

Sie können auch die beiden, ändern name kombinieren eine Funktion sein, dass standardmäßig Anrufe typeid(T).name() und dann auch nur für jene Fälle spezialisiert, wo das ist nicht akzeptabel.

Andere Tipps

Die Lösung ist

typeid(T).name()

, die zurück std :: type_info .

typeid(T).name() definiert Implementierung und bietet keine Garantie für den Menschen lesbaren String.

cppreference.com :

  

Gibt eine Implementierung definiert nullterminierten Zeichenfolge   mit dem Namen des Typs. Es wird keine Garantie gegeben, in   Insbesondere kann die zurückgegebene Zeichenfolge für verschiedene Arten identisch sein und   ändern zwischen Anrufungen des gleichen Programms.

     

...

     

Mit Compilern wie gcc und Klirren, kann die zurückgegebene Zeichenfolge durch c werden verrohrt ++ filt -t Menschen lesbare Form umgewandelt werden.

Aber in einigen Fällen gcc kehrt nicht richtig String. Zum Beispiel auf meinem Rechner habe ich gcc whith -std=c++11 und innerhalb Template-Funktion typeid(T).name() kehrt für "j" "unsigned int". Es ist so verstümmelten Namen genannt. Um echte Typnamen zu erhalten, verwenden abi :: __ cxa_demangle () Funktion (gcc only):

#include <string>
#include <cstdlib>
#include <cxxabi.h>

template<typename T>
std::string type_name()
{
    int status;
    std::string tname = typeid(T).name();
    char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status);
    if(status == 0) {
        tname = demangled_name;
        std::free(demangled_name);
    }   
    return tname;
}

Wie Bunkar typeid erwähnt (T) .name ist Implementierung definiert.

Um dieses Problem zu vermeiden, die Sie verwenden können Boost.TypeIndex Bibliothek.

Zum Beispiel:

boost::typeindex::type_id<T>().pretty_name() // human readable

Die Antwort von Logan Capaldo ist richtig, kann aber geringfügig vereinfacht werden, da es nicht notwendig ist, jedes Mal, um die Klasse zu spezialisieren. Man kann schreiben:

// in header
template<typename T>
struct TypeParseTraits
{ static const char* name; };

// in c-file
#define REGISTER_PARSE_TYPE(X) \
    template <> const char* TypeParseTraits<X>::name = #X

REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

Auf diese Weise können Sie auch die REGISTER_PARSE_TYPE Anweisungen in einem C ++ Datei setzen ...

Als Umformulierung von Andrey Antwort:

Die TypeIndex Bibliothek steigern kann verwendet werden, um Namen von Typen zu drucken.

Innerhalb einer Vorlage, das lesen könnte wie folgt

#include <boost/type_index.hpp>
#include <iostream>

template<typename T>
void printNameOfType() {
    std::cout << "Type of T: " 
              << boost::typeindex::type_id<T>().pretty_name() 
              << std::endl;
}

Ich kann es nur dort lassen. Wenn jemand es noch benötigen, dann können Sie diese verwenden:

template <class T>
bool isString(T* t) { return false;  } // normal case returns false

template <>
bool isString(char* t) { return true; }  // but for char* or String.c_str() returns true
.
.
.

Prüft nur geben Sie es nicht GET und nur für 1-Typ oder 2.

Wenn Sie eine pretty_name mögen, kann Logan Capaldo-Lösung nicht mit komplexer Datenstruktur befassen: REGISTER_PARSE_TYPE(map<int,int>) und typeid(map<int,int>).name() gibt mir ein Ergebnis von St3mapIiiSt4lessIiESaISt4pairIKiiEEE

Es gibt eine andere interessante Antwort mit unordered_map oder map kommt von https: // en. cppreference.com/w/cpp/types/type_index .

#include <iostream>
#include <unordered_map>
#include <map>
#include <typeindex>
using namespace std;
unordered_map<type_index,string> types_map_;

int main(){
    types_map_[typeid(int)]="int";
    types_map_[typeid(float)]="float";
    types_map_[typeid(map<int,int>)]="map<int,int>";

    map<int,int> mp;
    cout<<types_map_[typeid(map<int,int>)]<<endl;
    cout<<types_map_[typeid(mp)]<<endl;
    return 0;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top