Reputation: 143
I have an hierarchy of classes, the base class having a function to print the class name:
#include <iostream>
using namespace std;
class base
{
public:
virtual void print_name() { cout << typeid(*this).name() << endl; };
};
class derived1 : public base { };
class derived2 : public base { };
int main ()
{
base Base;
Base.print_name();
derived1 Derived1;
Derived1.print_name();
derived2 Derived2;
Derived2.print_name();
}
The output of the above is
class base
class derived1
class derived2
which is, in fact, platform dependent.
Is there a more or less standard way to "attach" some unique name to each class, so it could be used in printname()
making the output the same for all platforms (and independent of any changes made to real class names)?
Upvotes: 0
Views: 237
Reputation: 299730
You can effectively use type_info
for this.
type_info
supports a before
method conforming to a Weak Order, which allows its use in a std::map
(for example) as long as a user-supplied predicate is provided.
struct TypeInfoLess {
bool operator()(std::type_info const* lhs, std::type_info const* rhs) const {
return lhs->before(rhs);
}
};
struct AdditionalTypeInfo {
std::string name;
};
typedef std::map<std::type_info const*, AdditionalTypeInfo, TypeInfoLess> TypeInfoMap;
Then, you can just add/search types:
template <typename T>
void add(TypeInfoMap& map, T const& t, AdditionalTypeInfo const& ati) {
map[&typeid(t)] = ati;
}
template <typename T>
AdditionalTypeInfo const* find(TypeInfoMap const& map, T const& t) {
TypeInfoMap::const_iterator it = map.find(&typeid(t));
if (it == map.end()) { return 0; }
return &it->second;
}
int main() {
TypeInfoMap timap;
add(timap, timap, { "TypeInfoMap" });
if (AdditionalTypeInfo const* const ati = find(timap, timap)) {
std::cout << ati->name << "\n";
}
}
Note: it is then your responsability to add to the map every type that you might want.
Upvotes: 1
Reputation: 1300
You can use template functions (or traits classes) to get names:
template<typename T> const char * ClassName(T const * objPtr);
template<> const char * ClassName<derived1>(derived1 const *objPtr) { return "derived1"; }
template<> const char * ClassName<derived2>(derived2 const *objPtr) { return "derived2"; }
(Here the function parameter is used for template parameter matching only). And in calling code you can cast your pointer to specific class and use ClassName
derived1 * pDerived = static_cast<derived1*>(pPointer);
cout << ClassName(pDerived);
However this is all compile-time, and you will need some mechanism (like GUIDs) to identify a class at runtime (to cast it to the right pointer type). If your classes have none and you can't modify them, than I can't help you;( But you can use traits technique to localize your platform-dependent type selection code.
Upvotes: 0
Reputation: 363487
Sure:
class base {
public:
virtual char const *name() const { return "base"; }
};
class derived1 : public base {
public:
virtual char const *name() const { return "derived1"; }
};
However, if you do not override name
in a class, its name will be that of its superclass. That may be a bug or a feature, depending on your use case. If it's a bug, then you can add some runtime checks to make sure the method is overridden:
virtual char const *name() const {
if (typeid(*this) != typeid(base))
throw std::logic_error("name() not overridden");
return "base";
}
But you'll have to repeat this check in every implementation of name
that must be overridden.
Upvotes: 2