summaryrefslogtreecommitdiff
path: root/lout/identity.hh
blob: 7dcdbac4e867bcd8b86bf25e43145e92a0ab9802 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#ifndef __LOUT_OBJECTX_HH__
#define __LOUT_OBJECTX_HH__

#include "object.hh"
#include "container.hh"
#include "signal.hh"

/**
 * \brief Some stuff to identify classes of objects at run-time.
 */

namespace lout {

namespace identity {

/**
 * \brief Instances of classes, which are sub classes of this class, may
 *    be identified at run-time.
 *
 * <h3>Testing the class</h3>
 *
 * Since e.g. dw::Textblock is a sub class of IdentifiableObject, and
 * implemented in the correct way (as described below), for any given
 * IdentifiableObject the following test can be done:
 *
 * \code
 * identity::IdentifiableObject *o;
 * // ...
 * bool isATextblock = o->instanceOf(dw::Textblock::CLASS_ID);
 * \endcode
 *
 * \em isATextblock is true, when \em o is an instance of dw::Textblock,
 * or of a sub class of dw::Textblock. Otherwise, \em isATextblock is false.
 *
 * It is also possible to get the class identifier of an
 * identity::IdentifiableObject, e.g.
 *
 * \code
 * bool isOnlyATextblock = o->getClassId() == dw::Textblock::CLASS_ID;
 * \endcode
 *
 * would result in true, if o is an instance of dw::Textblock, but not an
 * instance of a sub class of dw::Textblock.
 *
 * <h3>Defining Sub Classes</h3>
 *
 * Each direct or indirect sub class of IdentifiableObject must
 *
 * <ul>
 * <li> add a static int CLASS_ID with -1 as initial value, and
 * <li> call registerName (\em name, &CLASS_ID) in the constructor, where
 *      \em name should be unique, e.g. the fully qualified class name.
 * </ul>
 *
 * After this, <i>class</i>::CLASS_ID refers to a number, which denotes the
 * class. (If this is still -1, since the class has not yet been instanciated,
 * any test will fail, which is correct.)
 *
 * <h3>Notes on implementation</h3>
 *
 * If there are some classes like this:
 *
 * \dot
 * digraph G {
 *    node [shape=record, fontname=Helvetica, fontsize=10];
 *    edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
 *          labelfontsize=10, color="#404040", labelfontcolor="#000080"];
 *    fontname=Helvetica; fontsize=10;
 *    IdentifiableObject [color="#a0a0a0"];
 *    A;
 *    B [color="#a0a0a0"];
 *    C;
 *    IdentifiableObject -> A;
 *    IdentifiableObject -> B;
 *    B -> C;
 * }
 * \enddot
 *
 * <center>[\ref uml-legend "legend"]</center>
 *
 * and first, an instance of A, and then an instance of C is created, there
 * will be the following calls of functions and constructors:
 *
 * <ol>
 * <li> %IdentifiableObject ();
 * <li> %registerName ("A", &A::CLASS_ID);
 * <li> %IdentifiableObject ();
 * <li> %registerName ("B", &B::CLASS_ID);
 * <li> %registerName ("C", &C::CLASS_ID);
 * </ol>
 *
 * From this, the class hierarchy above can easily constructed, and stored
 * in identity::IdentifiableObject::classesByName and
 * in identity::IdentifiableObject::classesById. See the code for details.
 *
 * N.b. Multiple inheritance is not supported, the construction of the
 * tree would become confused.
 */
class IdentifiableObject: public object::Object
{
private:
   class Class: public object::Object
   {
   public:
      Class *parent;
      int id;
      const char *className;

      Class (Class *parent, int id, const char *className);
   };

   static container::typed::HashTable <object::ConstString,
                                       Class> *classesByName;
   static container::typed::Vector <Class> *classesById;
   static Class *currentlyConstructedClass;

   int classId;

protected:
   void registerName (const char *className, int *classId);

public:
   IdentifiableObject ();

   virtual void intoStringBuffer(misc::StringBuffer *sb);

   /**
    * \brief Returns the class identifier.
    *
    * This is rarely used, rather, tests with
    * identity::IdentifiableObject::instanceOf are done.
    */
   int getClassId () { return classId; }
   
   /**
    * \brief Return the name, under which the class of this object was
    *    registered.
    */ 
   const char *getClassName() { return classesById->get(classId)->className; }

   bool instanceOf (int otherClassId);
};

} // namespace identity

} // namespace lout

#endif // __LOUT_OBJECTX_HH__