If you are looking for a totally general way to manipulate objects at runtime when you don't know their types at compile time in C++, you essentially need to:
- Define an interface (abstract base class with all pure virtual methods and no members) for each capability that a class might support.
- Each class must inherit virtually from all interfaces that it wants to implement (possibly among other classes).
Now, suppose pFoo
holds an interface pointer of type IFoo*
to some object x
(you don't need to know x
's concrete type). You can see whether this object supports interface IBar
by saying:
if (IBar* pBar = dynamic_cast<IBar*>(pFoo)) {
// Do stuff using pBar here
pBar->endWorldHunger();
} else {
// Object doesn't support the interface: degrade gracefully
pFoo->grinStupidly();
}
This approach assumes you know all relevant interfaces at compile time -- if you don't, you won't be able to use normal C++ syntax for calling methods anyway. But it's hard to imagine a situation where the calling program doesn't know what interfaces it needs -- about the only case I can think of would be if you want to expose C++ objects via an interactive interpreter. Even then, you can devise an (ugly, maintenance-intensive) way of shoehorning this into the above paradigm, so that methods can be called by specifying their names and arguments as strings.
The other aspect to consider is object creation. To accomplish this without knowing concrete types, you'll need a factory function, plus unique identifiers for classes to specify which concrete class you want. It's possible to arrange for classes to register themselves with a global factory upon startup, as described here by C++ expert Herb Sutter -- this avoids maintaining a gigantic switch
statement, considerably easing maintenance. It's possible to use a single factory, though this implies that there is a single interface that every object in your system must implement (the factory will return a pointer or reference to this interface type).
At the end of the day, what you wind up with is basically (isomorphic to) COM -- dynamic_cast<IFoo*>
does the same job as QueryInterface(IID_IFoo)
, and the base interface implemented by all objects is equivalent to IUnknown
.