Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: emulating RTTI

I've got a class hierarchy as this one:

class A        { }                            //
class AA  : A  { }                            //          A
class AAA : AA { }                            //        /   \
class AAB : AA { }                            //      AA     AB
class AB  : A  { }                            //     / \     / \
class ABA : AB { }                            //   AAA AAB ABA ABB
class ABB : AB { }                            //

I'd like to emulate RTTI (without using it, of course) for this ierarchy, in a way that, given a pointer/reference to A, I can find out its actual type (similarly to what typeid does), as an integer identifying the class.

Moreover I'd like that the set of integers identifying my types is contiguous and goes from 0 to N-1 (from 0 to 6 in my example):

class A        { virtual int t(){return 0;} } //
class AA  : A  { virtual int t(){return 1;} } //            A(0)
class AAA : AA { virtual int t(){return 2;} } //          /      \
class AAB : AA { virtual int t(){return 3;} } //      AA(1)       AB(4)
class AB  : A  { virtual int t(){return 4;} } //     /   \        /    \
class ABA : AB { virtual int t(){return 5;} } // AAA(2) AAB(3) ABA(5) ABB(6)
class ABB : AB { virtual int t(){return 6;} } //

(the order doesn't really matter: A::t could return 3 and AAB::t 0, for example.


Is it possible to let the compiler assign the indexes to my classes?

I think that CRTP could help me; something like:

class X : A, AssignFirstAvailableIndex< X > { }

but I'm not good enough with templates. How could I implement that AssignFirstAvailableIndex template class?

(of course the compiler can see all of the classes at compile time)

like image 386
peoro Avatar asked Dec 04 '10 11:12

peoro


1 Answers

There is a standard method to implement what you need. Standard locale facets use it to identify themselves. Consider examining standard header "locale".

class Base {
  public:
  // Being constructed contains a new unique identifier
  class Id {
    // An id factory returns a sequence of nonnegative ints
    static int allocate() {
      static int total = 0;
      return total++;
    }
    int _local;
    public:
    Id(): _local(allocate()) {}
    int get() const {return _local;}
  };
  //Child classes should make this function return an id generated by Base::Id constructed as static member.
  virtual int id() const = 0;
};

class Child1{
  public:
  static const Base::Id _id; 
  virtual int id() { return _id.get(); }
};

class Child2 {
  public:
  static const Base::Id _id; 
  virtual int id() { return _id.get(); }
};

class Child3 {
  public:
  static const Base::Id _id; 
  virtual int id() { return _id.get(); }
};

Static members might be initialized in implementation files or be templated to allow instantiation directly from headers or be refactored for lazy initialization.

like image 64
Basilevs Avatar answered Nov 15 '22 19:11

Basilevs