C++ Runtime Type System
I’ve finally had a free day to extract my C++ runtime type system and make it stand-alone. I’ve uploaded it here (17KB zip) for anyone interested. Feel free to take it for whatever purpose and improve it. I’m offering it completely "as is," meaning I don’t intend to make changes or offer support.
I did make a few improvements in the macro system though. It figures out the member types, sizes, offsets, array counts, and any derived-from or referenced types automatically. The only thing it can’t get from the compiler right now is the list of members to enumerate or it could probably be completely automatic.
It’s using template specialization and static initializers for all of the magic. All of this happens at static initialization time, which isn’t much of a performance hit, but it’s worth remembering that it’s not 100% pre-compiled information right now. But on the plus side, only the types your code asks about (directly and indirectly) will ever be reflected this way, so the cost almost nothing.
The one downside, apart from having to enumerate the members in code, is that classes must have a static "default" object stored with the RTTI "Type" struct, meaning all reflected classes also need to have a public default constructor available. Singletons are not generally friendly to that idea, so keep that in mind. FYI, none of the data in that default object is used at the moment, but the object sticks around until the program is closed and its destructors are called just the same. It’s needed because it’s very hard to make opaque objects like int and float act the same way as your classes for initializing type info.
It’s a reasonable tradeoff, IMO, considering there is zero per-instance storage and almost zero overhead, apart from a virtual function table if you use normal virtual functions or enable dynamic typing (which uses normal virtual functions).
Take a look at the Reflection.cpp file for some simple simple examples. Templated types should work as well using the supplied macros.
This compiles with Visual Studio 2008. I’ll try it on the new MS compiler soon. The code should work just fine in 2005, but the Solution is for 2008.
Sample Code:
struct Foo
{
char one;
double two[4];
void InitType()
{
INIT_STRUCT(Foo);
INIT_MEMBER(one);
INIT_MEMBER(two);
}
Foo() { }
};
class Test : public Foo
{
private:
int three;
float four;
Test* next;
Foo foo[2];
public:
void InitType()
{
INIT_DERIVED(Test,Foo);
INIT_MEMBER(three);
INIT_MEMBER(four);
INIT_MEMBER(next);
INIT_MEMBER(foo);
}
Test() { };
};
int _tmain(int argc, _TCHAR* argv[])
{
cout << TYPE(Foo) << "\n";
cout << TYPE(Test) << "\n";
return 0;
}
Outputs:
struct Foo
{
byte one;
double two[4]; /* internally: double* */
};
struct Test: public Foo
{
byte one;
double two[4]; /* internally: double* */
int three;
float four;
Test* next;
Foo foo[2]; /* internally: Foo* */
};
Yes, the "public Foo" bit is redundant with theinherited members, but I opted to be verbose for storing derived types. The parentage is there for "isOfType" type operations.
[...] Updated here [...]
Hi,
I noted that in the Type class you use std::string for storing the name of the class/member. Why not just use const char* ? to make the RTTI system more independent?
Overall a nice, simple and powerful little system.
Thanks. std::string is pretty portable nowadays. But it’s easy to replace. I tend to not use const char* too much these days because it’s just too prone to bugs.