Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeID for derived classes of a common base class

Tags:

c++

types

typeid

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

like image 919
Dan Avatar asked Nov 26 '10 01:11

Dan


3 Answers

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.,

like image 159
Cheers and hth. - Alf Avatar answered Oct 03 '22 04:10

Cheers and hth. - Alf


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.

like image 40
Yttrill Avatar answered Oct 03 '22 04:10

Yttrill


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...

like image 30
Steve Jessop Avatar answered Oct 03 '22 03:10

Steve Jessop