I have a C++ class hierarchy defined by inheritance and I store a description of this hierarchy in it that I can later use for introspection. I would like to know if there is a more efficient or cleaner way to define this than the way I currently do it. Here is a stripped down version of my code
// in header file (hpp)
struct Type
{
Type( const string& n, const Type* p = nullptr ) : name(n), parent(p) {}
const string name;
const Type* parent;
};
class Base
{
public:
static const Type m_type;
virtual const Type& type() const { return m_type; }
};
class Derived : public Base
{
public:
static const Type m_type;
const Type& type() const { return m_type; }
};
// in implementation file (cpp)
const Type Base::m_type( "Base" );
const Type Derived::m_type( "Derived", &Base::m_type );
Not necessarily more efficient but think whether you actually want to require a common base class. An alternative approach uses a global type information registry. Then querying a type’s type information is done via TypeInfo::get(my_variable)
or TypeInfo::get(typeid(my_type))
.
This has the advantage that it also works with existing types, which just need to be added to this type info registry.
Internally, the registry would use a map from std::type_info
to Type
or similar. The following is a proof of concept. Unfortunately the code doesn’t compile on either clang or GCC. Based on the error messages, I’m suspecting a bug but I could also be wrong …
struct Type {
std::string name;
std::vector<Type*> parents;
// TODO Extend by fully-qualified name (namespace) etc.
template <typename... T>
Type(std::string&& name, T*... parents)
: name(name), parents{parents...} { }
};
struct TypeInfo {
template <typename T>
static Type const& get(T const&) { return get(typeid(T)); }
template <typename T>
static Type const& get() { return get(typeid(T)); }
static Type const& get(std::type_info const& info) {
auto i = types.find(info);
if (i == types.end())
throw unknown_type_error(info.name());
return i->second;
}
template <typename T>
static void register_type(Type&& type) {
types.insert(std::make_pair(typeid(T), type));
}
typedef std::unordered_map<std::type_info, Type> type_dir_t;
static type_dir_t types;
};
Full code available as gist on github.
Having a common base class for logically unrelated classes is generally frowned upon in C++, although it could be argued that this is similar to CRTP / mixins, in which common base classes are encouraged. So I’d say that there isn’t necessarily anything wrong with the approach if you don’t care for existing types.