tags:

views:

226

answers:

4

Say B and C are derived from A. I want to be able to test whether any two instances of classes derived from A are instances of the same class, that is, whether A* foo and A* bar both point to B instances, without using RTTI. My current solution is something like this:

class A {
protected:

    typedef uintptr_t Code;
    virtual Code code() const = 0;

}; // class A


class B : public A {
protected:

    virtual Code code() const { return Code(&identity); }

private:

    static int identity;

}; // class B


class C : public A {
protected:

    virtual Code code() const { return Code(&identity); }

private:

    static int identity;

}; // class C

Using this method, operator== can simply test first.code() == second.code(). I'd like to remove the literal identity from the derived classes and have the code found automatically by A, so that not all of the derived classes have to repeat this idiom. Again, I would strongly prefer not to use RTTI. Is there any way to do this?

Note: I have seen recent questions [1] and [2], and this is not a duplicate. Those posters want to test the contents of their derived classes; I merely want to test the identities.

A: 

You can have the Base class to take id as a constructor parameter and implement the identity() function in base class itself. Then there is no need to repeat the code in derived classes. In the derived class constructor, you can do something like derived::derived(): base(0) Sample Code:

class A
{
public:
    A(int n) : m_id(n)
    {
    }
    virtual ~A(){}

    virtual int id() const 
    {
        return m_id;
    }

private:
    int m_id;
};

class B : public A
{
public:
    B() : A(0)
    {
    }
};
Naveen
Good idea, but it still necessitates that all of the derived classes do extra work, and introduces the possibility of collisions. Also, ideally, the identity should require no storage.
Jon Purdy
I think Naveen is right 1+.
Ashish
This is poor because it tags every instance with the type tag, when it is really only necessary to associate the tag with the class.
Dominic Cooney
A: 

you can use the both macro __FILE__ __LINE__ as your code
this will avoid the collision problem
you can map this values to an int

jojo
Implicit template instantiations will share the same `__FILE__` and `__LINE__` but be different classes, so this is imperfect. Interesting hack though.
Dominic Cooney
It'd have to be `__FILE__ + __LINE__` or similar, and it'd have to appear once for each derived class, which is exactly what I'm trying to avoid. It would also use storage, and way more than just an `int`.
Jon Purdy
+2  A: 

You should just use RTTI instead of reinventing the wheel.

If you insist on not using RTTI, you could use CRTP and a function-local static variable to avoid having to write the function to every derived class. Adapt from this example code I wrote for Wikipedia: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern#Polymorphic_copy_construction

Another alternative is reading the vtable pointer (via this and pointer arithmetics), but that would depend on both the compiler and the platform, so it is not portable.

Tronic
Your suggestion to use CRTP worked perfectly to eliminate duplication in the derived classes. Thank you! Now I'm just twiddling with ways to get it to take up no space.
Jon Purdy
Notice that the function itself (program code) takes much more space than the static variable within it does.
Tronic
+2  A: 

Your idea is on the right track; maybe you can eliminate some boilerplate with a template:

class TypeTagged {
public:
  virtual Code code() const = 0;
}

template <class T>
class TypeTaggedImpl: public virtual TypeTagged {
public:
  virtual Code code() const { return Code(&id); }
private:
  static int id;
}

Then your client classes just need to be declared like this:

class A: public TypeTaggedImpl<A> { ... }

class B: public A, public TypeTaggedImpl<B> { ... }

The different instantiations of TypeTagged mean that the types have different id fields and hence different IDs; the virtual base type means that the code for the most derived type gets returned.

Dominic Cooney
This is effectively the same as Tronic's answer, but +1 for providing an example.
Jon Purdy