views:

1985

answers:

2

I'm writing some template classes for parseing some text data files, and as such it is likly the great majority of parse errors will be due to errors in the data file, which are for the most part not written by programmers, and so need a nice message about why the app failed to load e.g. something like:

Error parsing example.txt. Value ("notaninteger")of [MySectiom]Key is not a valid int

I can work out the file, section and key names from the arguments passed to the template function and member vars in the class, however I'm not sure how to get the name of the type the template function is trying to convert to.

My current code looks like, with specialisations for just plain strings and such:

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 rather not have to make specific overloads for every type that the data files might use, since there are loads of them...

Also I need a solution that does not incur any runtime overhead unless an exception occurs, i.e. a completely compile time solution is what I want since this code is called tons of times and load times are already getting somewhat long.

EDIT: Ok this is the solution I came up with:

I have a types.h containg the following

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

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

Then I can use the DEFINE_TYPE_NAME macro to in cpp files for each type I need to deal with (eg in the cpp file that defined the type to start with).

The linker is then able to find the appropirate template specialisation as long as it was defined somewhere, or throw a linker error otherwise so that I can add the type.

+7  A: 

A runtime solution is

typeid(T).name()

which returns a (compiler-dependent, I believe) name of the type in question. It won't be called unless an exception is thrown (in your code), so it may satisfy your criteria.

Jesse Beder
Keep in mind that it's compliant to return the same string for every type (though I don't think any compiler would do that).
Motti
+5  A: 

Jesse Beder's solution is likely the best, but if you don't like the names typeid gives you (I think gcc gives you mangled names for instance), you can do something like:

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

And then use it like

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

EDIT:

You could also combine the two, change name to be a function that by default calls typeid(T).name() and then only specialize for those cases where that's not acceptable.

Logan Capaldo
Note: This code will not compile if you forget to define REGISTER_PARSE_TYPE for a type that you use. I have used a similar trick before (in code without RTTI) and it has worked very well.
Tom Leys
I had to move name outside of the struct in g++ 4.3.0 due to "error: invalid in-class initialization of static data member of non-integral type 'const char *'"; and, of course, the keyword 'struct' is needed between <> and TypeParseTraits and the definition should be terminated with a semicolon.
fuzzyTew
Well the leaving the semicolon out was intentional, to force you to use it at the end of the macro invocation, but thanks for the corrections.
Logan Capaldo
Great stuff! Thanks for sharing! Now I understood how to keep track of types without RTTI :) That it was that simple! It even can be done without templates (but not related to this question) :)
Viet