I don't think there's any way to generate the indexes at compile time, except by using an enum
(which I'd consider a perfectly reasonable approach). I'm not sure that a template would help, because templates are purely functional and there's nowhere to store any global state (i.e., the current index) the except in the name of the templated type itself (which is exactly what you're trying to avoid).
If you really want integer IDs, it's probably easiest to set them up at runtime rather than trying too hard.
Firstly, have an object that represents a type, and use the typical hand-made-RTTI approach: have each class have a static instance of that object, and have its virtual get-type-info function return a pointer to that object. So each class would have a little bit of code in it, like this:
static TypeInfo ms_type_info;
virtual const TypeInfo *GetTypeInfo() const {
return &ms_type_info;
}
And you'd define the type info, putting into the <<whatever you info you want>>
section whatever info the TypeInfo
stores off to make it better than the compiler's RTTI;
TypeInfo WhateverClass::ms_type_info(<<whatever info you want>>);
(Every implementation I've seen of this uses a macro to automate the creation of these two bits of text; a bit ugly, but your options are limited, and it's better than typing it out.)
The TypeInfo
struct itself would look a bit like this:
struct TypeInfo {
int type_index;
TypeInfo *next;
TypeInfo(<<whatever>>) {<<see below>>}
};
If the reader would prefer get and set functions, it could have those.
The TypeInfo
object should be static to the class, not the function, because the intent is to create a list of all TypeInfos
. You have two options here. The automatic one is to have each TypeInfo
, in the constructor that was left blank above, add itself to some kind of global linked list; the other one is to have a big function that adds the ones it wants to the global list manually.
Then, on startup, run through the TypeInfo
objects and assign each an index. Something like this would do, assuming that there's a TypeInfo *g_first_type_info
that points to the first type info in the list:
int next_type_index=0;
for(TypeInfo *ti=g_first_type_info;ti;ti=ti->next)
ti->type_index=next_type_index++;
Now, when you want your integer ID, you can retrieve it easily:
object->GetTypeInfo()->type_index;
You could implement t
easily enough now:
virtual int t() const {
return ms_type_info.type_index;
}
Full disclosure of the problems I can think of:
The type indexes aren't set at compile time, so if you build up arrays you'll need to build them at runtime. This can be a waste of code if your arrays would otherwise be compile time constants - however for compilers that don't support the C99-style array initialization syntax, this can sometimes make the code more readable.
If you like to do everything in global constructors before main
starts, you may come unstuck, as the TypeInfo
objects are just ordinary global objects and (in this situation anyway) aren't properly ready for use anyway until the type indexes have been assigned.
Linkers have a tendency to strip out global objects that don't appear to get used, so type info objects in static libraries may never add themselves to the list. So you should bear in mind that whilst the automatic registration approach works well, when it doesn't work, it doesn't, so you may end up having to register things manually anyway.