I'm attempting to implement some mechanism in C++ whereby all classes derived from a common base class are assigned a unique "class ID". For example:
class BaseClass
{
//...
public: unsigned int GetID( void );
//...
};
class DerivedClass : public BaseClass
{
}
Class DerivedClass, and all other children of BaseClass, should be able to return unique identifiers without any additional code added to DerivedClass...C++ is making this rather difficult for me, however. Any ideas would be appreciated.
Thanks in advance! ---Dan
You don't indicate that you're familiar with typeid
and dynamic_cast
.
Chances are they solve your problem.
If not then please describe the reason why not.
Cheers & hth.,
You should listen to Alf :) Here's my analysis: in the pure world, identification of implementations threatens virtual function polymorphism, can't be required and shouldn't be needed.
In the dirty world of real programming, you may have some reasons for unique identification such as marshaling data to disk, identifying diagnostic messages, tracing control flow, accumulating usage statistics, etc.
If your thoughts are pure, your design is wrong. Go away and figure out why you cannot require a unique id.
If you thoughts are corrupted by reality, then you are already willing to pay a price in performance and memory to meet your requirements, and then by specification the cost of using the built-in language features is worth paying, since it is pretty much the only way to achieve your goal of providing a non-invasive identification service. By non-invasive I mean you don't need to add anything to each derived class. Clearly something must be added, so if you're unwilling to do so, you have little choice but to accept that which the compiler adds for you.
The main gotcha here is that if you're using dynamically loaded shared libraries (DLLS) RTTI may not work as expected. This doesn't just affect typeid adversely, it can also prevent catching exceptions you expect to get caught (I have been bitten!). Some care may be needed to ensure vtables and other RTTI are uniquely created. This may mean, for example, that if the vtables hook on your destructor, it is not inline, because it may be generated in more than one place in that case, destroying uniqueness. Some hacking about may be necessary here in the absence of ISO Standardisation support for dynamic loading.
As Alf says, this shouldn't be necessary. typeid
already gives a unique class identifier, although the identifier isn't an integer. Just for laughs, if I'm allowed to relax the "common base class" condition then:
inline unsigned int counter() {
static unsigned int count = 0;
return ++count;
}
struct BaseClass {
virtual unsigned int GetID() = 0;
virtual ~BaseClass() {}
};
template <typename D>
struct IntermediateClass : BaseClass {
virtual unsigned int GetID() {
static unsigned int thisid = counter();
return thisid;
}
};
// usage
struct Derived : IntermediateClass<Derived> {
...
};
You'd need to add thread-safety in counter
if it's to be used in multi-threaded programs.
Obviously the ID is only unique within a given run of the program.
It gets a bit hairy if your inheritance hierarchy is deep, and if you have lots of constructors with different signatures for different classes, because you need to insert IntermediateClass between each derived class and its direct base class. But you can always bail out of all that as follows:
inline unsigned int counter() {
static unsigned int count = 0;
return ++count;
}
struct BaseClass {
virtual unsigned int GetID() = 0;
virtual ~BaseClass() {}
};
template <typename D>
unsigned int ThisID(const D *) {
static unsigned int thisid = counter();
return thisid;
}
// usage
struct Derived : BaseClass {
// this single line pasted in each derived class
virtual unsigned int GetID() { return ThisID(this); }
...
};
I guess there's an "opportunity" for a new language feature here: a virtual function which is defined in the base class as a template function with one "typename" template parameter, and which is automatically overridden in each derived class using that derived class as the template argument. Imaginary syntax, since virtual template functions are illegal:
struct BaseClass {
template <typename Derived>
virtual unsigned int GetID() {
static unsigned int thisid = counter();
return thisid;
}
virtual ~BaseClass() {}
};
Hard to justify a language feature on the basis of wanting to re-implement RTTI ourselves, mind...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With