I'm working with a simple object model in which objects can implement interfaces to provide optional functionality. At it's heart, an object has to implement a getInterface
method which is given a (unique) interface ID. The method then returns a pointer to an interface - or null, in case the object doesn't implement the requested interface. Here's a code sketch to illustrate this:
struct Interface { };
struct FooInterface : public Interface { enum { Id = 1 }; virtual void doFoo() = 0; };
struct BarInterface : public Interface { enum { Id = 2 }; virtual void doBar() = 0; };
struct YoyoInterface : public Interface { enum { Id = 3 }; virtual void doYoyo() = 0; };
struct Object {
virtual Interface *getInterface( int id ) { return 0; }
};
To make things easier for clients who work in this framework, I'm using a little template which automatically generates the 'getInterface' implementation so that clients just have to implement the actual functions required by the interfaces. The idea is to derive a concrete type from Object
as well as all the interfaces and then let getInterface
just return pointers to this
(casted to the right type). Here's the template and a demo usage:
struct NullType { };
template <class T, class U>
struct TypeList {
typedef T Head;
typedef U Tail;
};
template <class Base, class IfaceList>
class ObjectWithIface :
public ObjectWithIface<Base, typename IfaceList::Tail>,
public IfaceList::Head
{
public:
virtual Interface *getInterface( int id ) {
if ( id == IfaceList::Head::Id ) {
return static_cast<IfaceList::Head *>( this );
}
return ObjectWithIface<Base, IfaceList::Tail>::getInterface( id );
}
};
template <class Base>
class ObjectWithIface<Base, NullType> : public Base
{
public:
virtual Interface *getInterface( int id ) {
return Base::getInterface( id );
}
};
class MyObjectWithFooAndBar : public ObjectWithIface< Object, TypeList<FooInterface, TypeList<BarInterface, NullType> > >
{
public:
// We get the getInterface() implementation for free from ObjectWithIface
virtual void doFoo() { }
virtual void doBar() { }
};
This works quite well, but there are two problems which are ugly:
A blocker for me is that this doesn't work with MSVC6 (which has poor support for templates, but unfortunately I need to support it). MSVC6 yields a C1202 error when compiling this.
A whole range of classes (a linear hierarchy) is generated by the recursive
ObjectWithIface
template. This is not a problem for me per se, but unfortunately I can't just do a singleswitch
statement to map an interface ID to a pointer ingetInterface
. Instead, each step in the hierarchy checks for a single interface and then forwards the request to the base class.
Does anybody have suggestions how to improve this situation? Either by fixing the above two problems with the ObjectWithIface
template, or by suggesting alternatives which would make the Object/Interface framework easier to use.
What about something like that ?
You may want something more elaborate than
type_info const*
if you want to do this across dynamic libraries boundaries. Something likestd::string
andtype_info::name()
will work fine (albeit a little slow, but this kind of extreme dispatch will likely need something slow). You can also manufacture numeric IDs, but this is maybe harder to maintain.Storing hashes of type_infos is another option:
and use
FooInterface::hash()
when you add the interface, and the virtualInterface::type()
when you query.