397
25
IntrospectionforC++GameEngines
Jon Watte
IMVU, Inc.
25.1Introduction
This gem describes a mechanism for adding general-purpose introspection of
data types to a C++ program, using a minimum of macros, mark-up, or repetition.
Introspection is the capability of a computer program to look at its own data and
make modifications to it. Any high-level language of note has a rich introspec-
tion API, generally as part of an even larger reflection API, but users of C++
have had to make do with the built-in class
type_info ever since the early days.
A conversation with the introspection system of a language such as C#, Java, or
Python might look something like this:
“Hello, introspection system, I’d like you to tell me a bit about this here
piece of data I have!”
“Why, certainly, game program! It’s a data structure you’d like to call a
TreasureChest.”
“That’s useful to know, but what I really want to know is whether it has a
property called ‘
Position’?”
“Yes, it does! It’s of type
float3, containing the position in the world.”
“That’s great! Actually, now that I think about it, the designer just
clicked on it, and wants to edit all the properties. What are they?”
“Well, we’ve got ‘
Name’, which is a string, and ‘Contents’, which is a
list of references to
Object templates, and ‘Model’, which is a string
used to reference the 3D mesh used to render the object, and …”
In code, it looks something more like Listing 25.1 (using C#/.NET).
398 25.IntrospectionforC++GameEngines
string typeName = theObject.GetType().Name;
PropertyInfo info = theObject.GetType().GetProperty("Position");
/* ... */
foreach (PropertyInfo pi in theObject.GetType().GetProperties())
{
theEditor.AddProperty(theObject, pi.Name,
new GetterSetter(pi.ReflectedType));
}
Listing 25.1. Properties in a dynamic language.
So, what can we do in C++? Using only the standard language and library,
we can’t do much. You can find out the name of a type (using
typeid), and the
size of a type (using
sizeof), and that’s about it. You can also find out whether a
given object instance derives from a given base class (using the horribly expen-
sive
dynamic_cast<>), but only if the type has a virtual table, and you can’t iter-
ate over the set of base classes that it derives from, except perhaps by testing
against all possible base classes. Despite these draw-backs, C++ game program-
mers still need to deal with data, display it in editors, save it to files, send it over
networks, wire compatible properties together in object/component systems, and
do all the other data-driven game development magic that a modern game engine
must provide.
Trying to solve this problem, many engines end up with a number of differ-
ent mechanisms to describe the data in a given object, entity, or data structure.
For example, you may end up having to write code that’s something like what is
shown in Listing 25.2.
class TreasureChest : public GameObject
{
private:
/* properties */
string name;
float3 position;
list<ObjectRef> contents;
string model;
public:
25.1Introduction 399
/* saving */
void WriteOut(Archive &ar)
{
ar.beginObject("TreasureChest");
GameObject::WriteOut(ar);
ar.write(name);
ar.write(position);
ar.write(contents.size());
for (list<ObjectRef>::iterator ptr(contents.begin()),
end(contents.end()); ptr != end; ++ptr)
{
ar.write(*ptr);
}
ar.write(model);
ar.endObject();
}
/* loading */
void ReadIn(Archive &ar)
{
ar.beginObject("TreasureChest");
GameObject::ReadIn(ar);
ar.read(name);
ar.read(position);
size_t size;
ar.read(size);
while (size-- > 0)
{
contents.push_back(ObjectRef());
ar.read(contents.back());
}
ar.read(model);
ar.endObject();
}
#if EDITOR
Get Game Engine Gems 2 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.