S
stephan beal
Good afternoon, C++ers,
This weekend i came across a fairly project-neutral trick which can be used
to map C++ class names to their human-readable equivalents (a-la QObject's
className() method), without having to add methods to a client interface
and without relying on (e.g.) typeid(T).name().
i understand that this may not be strictly a language-related post, but i
think the solution is "template-enough" to be considered topical here.
Sample usage:
#include "class_name.h"
....
CLASS_NAME(MyClass);
....
cout << class_name<MyClass>() << endl;
or
cout << class_name<MyClass>::name() << endl;
the code:
#ifndef CLASS_NAME_H_INCLUDED
#define CLASS_NAME_H_INCLUDED 1
#include <cassert>
namespace { // anonymous ns, important for linking reasons.
/**
A utility class to provide human-usable class names, available at
runtime.
It MUST be specialized to work.
Call the CLASS_NAME(Type) or
CLASS_NAME_ALIAS(Type,AliasForType) macros from somewhere
in the global namespace (in a header, not an impl file) to
register a class_name<Type> specialization for Type. It may
only be called one time per Type per compilation unit, or
you will get specialization collisions at compile time.
Ideally, CLASS_NAME() is called from a class' header file.
Caveats:
- template types with commas in the names will break the
macro and...
- typedef'd names will get their typedef'd name, not their
real name. Maybe a feature, maybe not.
*/
template <class T> struct class_name
{
// static const char * classname = "...";
// ^^^^ we can't initalized a non-integral type this way,
thus the bloat show below...
class_name(){}
~class_name(){}
static const char * name()
{
assert( 0 /* this class_name<> is unspecialized!
*/ );
return "error::class_name<unspecialized>";
}
operator const char * () const
{
return name();
}
};
} // namespace
#define CLASS_NAME(Type) CLASS_NAME_ALIAS(Type,Type)
#define CLASS_NAME_ALIAS(Type,Alias) \
namespace {\
template <> struct class_name<Type> {\
class_name(){}; ~class_name(){}; \
static const char * name() {return # Alias; }\
operator const char * () const { return name(); }\
};}
#endif // CLASS_NAME_H_INCLUDED
i now use this as the basis for a classloader.
Enjoy...
--
----- stephan beal
Registered Linux User #71917 http://counter.li.org
I speak for myself, not my employer. Contents may
be hot. Slippery when wet. Reading disclaimers makes
you go blind. Writing them is worse. You have been Warned.
This weekend i came across a fairly project-neutral trick which can be used
to map C++ class names to their human-readable equivalents (a-la QObject's
className() method), without having to add methods to a client interface
and without relying on (e.g.) typeid(T).name().
i understand that this may not be strictly a language-related post, but i
think the solution is "template-enough" to be considered topical here.
Sample usage:
#include "class_name.h"
....
CLASS_NAME(MyClass);
....
cout << class_name<MyClass>() << endl;
or
cout << class_name<MyClass>::name() << endl;
the code:
#ifndef CLASS_NAME_H_INCLUDED
#define CLASS_NAME_H_INCLUDED 1
#include <cassert>
namespace { // anonymous ns, important for linking reasons.
/**
A utility class to provide human-usable class names, available at
runtime.
It MUST be specialized to work.
Call the CLASS_NAME(Type) or
CLASS_NAME_ALIAS(Type,AliasForType) macros from somewhere
in the global namespace (in a header, not an impl file) to
register a class_name<Type> specialization for Type. It may
only be called one time per Type per compilation unit, or
you will get specialization collisions at compile time.
Ideally, CLASS_NAME() is called from a class' header file.
Caveats:
- template types with commas in the names will break the
macro and...
- typedef'd names will get their typedef'd name, not their
real name. Maybe a feature, maybe not.
*/
template <class T> struct class_name
{
// static const char * classname = "...";
// ^^^^ we can't initalized a non-integral type this way,
thus the bloat show below...
class_name(){}
~class_name(){}
static const char * name()
{
assert( 0 /* this class_name<> is unspecialized!
*/ );
return "error::class_name<unspecialized>";
}
operator const char * () const
{
return name();
}
};
} // namespace
#define CLASS_NAME(Type) CLASS_NAME_ALIAS(Type,Type)
#define CLASS_NAME_ALIAS(Type,Alias) \
namespace {\
template <> struct class_name<Type> {\
class_name(){}; ~class_name(){}; \
static const char * name() {return # Alias; }\
operator const char * () const { return name(); }\
};}
#endif // CLASS_NAME_H_INCLUDED
i now use this as the basis for a classloader.
Enjoy...
--
----- stephan beal
Registered Linux User #71917 http://counter.li.org
I speak for myself, not my employer. Contents may
be hot. Slippery when wet. Reading disclaimers makes
you go blind. Writing them is worse. You have been Warned.