1

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.

Community
  • 1
  • 1
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • 3
    If you don't use RTTI, you're doomed to re-invent it. Imperfectly. – Hans Passant Feb 11 '10 at 06:25
  • Out of curiosity: why would you prefer to use this approach rather than using RTTI? – Laurence Gonsalves Feb 11 '10 at 06:26
  • @nobugz: I'm not re-inventing the whole thing; I'm emulating only the one feature I would use from the larger built-in feature. @Laurence: This project has to be able to run everywhere. I want no space overhead and no runtime overhead beyond comparing two integers. – Jon Purdy Feb 11 '10 at 06:37
  • 1
    RTTI doesn't have any features. It does only one thing, with *very* little overhead. – Hans Passant Feb 11 '10 at 06:48
  • 2
    @Jon: Have you actually _measured_ the "overhead" you're trying to avoid? Once you have virtual functions, RTTI has next to no overhead. The only one I can think of is one class object per class _type_. And unless your compiler allows you to turn off RTTI (as VC does), you'll have that anyway. – sbi Feb 11 '10 at 06:51
  • @sbi: Okay, okay, so maybe I'm cutting before I measure, but there's no harm in learning a bit while I'm at it. Good point about actually _turning off_ RTTI if I want it off. – Jon Purdy Feb 11 '10 at 06:55
  • @nobugz: Note that in template metaprogramming, you often need type information (i.e. type_traits) to take decisions at compile time, where RTTI is not applicable nor appropriate. – Sebastian Mach Feb 11 '10 at 09:27

4 Answers4

3

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
  • 10,250
  • 2
  • 41
  • 53
  • 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 Feb 11 '10 at 06:51
  • Notice that the function itself (program code) takes much more space than the static variable within it does. – Tronic Feb 11 '10 at 09:21
2

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
  • 6,317
  • 1
  • 26
  • 38
0

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
  • 74,600
  • 47
  • 176
  • 233
  • 1
    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 Feb 11 '10 at 06:20
  • 1
    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 Feb 11 '10 at 06:29
-1

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
  • 3,614
  • 1
  • 25
  • 21
  • 1
    Implicit template instantiations will share the same `__FILE__` and `__LINE__` but be different classes, so this is imperfect. Interesting hack though. – Dominic Cooney Feb 11 '10 at 06:30
  • 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 Feb 11 '10 at 06:31