I’ve written a simple traits class for associating string identifiers to specified types, it is used like this:
typedef someclass<double,44> someD;
template<> inline const char*
nametrait<someD>::name() {return "some_other_double";}
then I can statically and type-safely do
std::cout << someD::name()<
and I get some_other_double. This is to be used to wrap an old
database-like structure with integer keys to a more typesafe
treatment, where each field is represented by their own type, and the
some_other_double represents the field name in the database.
This works fine, and I also get a reasonable compilation error when no
name is set (find my full code at the end). However, I wonder if there’s
some better pattern for achieving the same effect. I’ll define many
object like these, and what I’m really after is something more like
typedef someclass<double,44,"some_other_double"> someD;
but litteral string constants are not permitted to be used like this (not even in c++11 I think). If
I was alone working on this code I would probably use a macro, but I’m
not that keen on defending it
#define namedtype(ss,tt,id,nn,newname)\
typedef ss<tt,id> newname; \
template<> inline const char* nametrait<newname>::name() {return nn;}
I also experimented with specifying static name constants, along the lines of:
template<>
const char * someclass<double,44>::name = "some_other_double";
but I think it’s not possible to make sure they are defined once and
only once without even more text than the above (I need to force
template instantiation for each declared one in some cpp file).
I prefer compile time errors before link errors.
Here’s my code:
// file: mytypes.h (I omit the include guard)
// this is the default which generates an error message on looking up
// MISSING_DEFINITION_OF_name
template <typename T>
class nametrait
{
public:
static inline const char* name() {return T::MISSING_DEFINITION_OF_name();};
};
template <typename T,int identifier>
class someclass {
private:
someclass(){};
public:
static const char * name() {return nametrait<someclass>::name() ;};
};
typedef someclass<double,44> someD;
template<> inline const char* nametrait<someD>::name() {return "some other double";}
typedef someclass<int,55> someI;
template<> inline const char* nametrait<someI>::name() {return "some specific int";}
typedef someclass<char,22> someC;
template<> inline const char* nametrait<someC>::name() {return "some specific char";}
typedef someclass<char,5> someC;
template<> inline const char* nametrait<someC>::name() {return "some other char";}
use:
// file:: main.cpp
#include mytypes.h
int main(){
std::cout << someI::name()<<std::endl;
std::cout << someD::name()<<std::endl;
std::cout << someC::name()<<std::endl;
std::cout << someC2::name()<<std::endl;
}
Questions:
- Does anyone have a better solution?
- Is my understanding correct that there’s normally not going to be any extra cost of the inline template method returning a string compared to static string constants? (for example using gcc with -g -O, which indeed does not imply -fno-inline).
I’m not sure if it is better, but this is slightly shorter: