views:

373

answers:

4

I have the convenient object factory template that creates objects by their type id names. The implementation is pretty obvious: ObjectFactory contains the map from std::string to object creator function. Then all objects to be created shall be registered in this factory.

I use the following macro to do that:

#define REGISTER_CLASS(className, interfaceName) \
   class className; \
   static RegisterClass<className, interfaceName> regInFactory##className; \
   class className : public interfaceName

where RegisterClass is

   template<class T, class I>
   struct RegisterClass
   {
      RegisterClass()
      {
         ObjectFactory<I>::GetInstance().Register<T>();
      }
   };

Usage

class IFoo
{
public:
   virtual Do() = 0;
   virtual ~IFoo() {}
}

REGISTER_CLASS(Foo, IFoo)
{
   virtual Do() { /* do something */ }
}

REGISTER_CLASS(Bar, IFoo)
{
   virtual Do() { /* do something else */ }
}

Classes are defined and registered in factory simultaneously.

The problem is that regInFactory... static objects are defined in .h files, so they will be added to every translation unit. The same object creator will be registered several times, and, more important, there will be a lot of redundant objects with static storage duration.

Is there any way to do such elegant registration (not to copy/paste class and interface names), but do not spread redundant static objects around the globe?

If a good solution needs some VC++ specific extensions (not conforming to C++ standard), I will be OK with that.

Thanks

+2  A: 

Why not change the macro so REGISTER_CLASS only registers the class without declaring it?

It would allow you to derive from another class while also implementing the interface, you could put the registration into your .cpp file where it's only in one compilation unit and it would minimize header dependencies (you no longer have to include the object factory header in all your public headers).

There is a VC++ extension that should cause the linker to pick just one of the globals and discard the unused ones. The variable in question needs to be globally visible (= no static, would cause linker errors if the compiler doesn't support the extension):

__declspec( selectany )

Usage would be like this:

#define REGISTER_CLASS(className, interfaceName) \
  class className; \
  __declspec( selectany ) \
    RegisterClass<className, interfaceName> regInFactory##className; \
  class className : public interfaceName
Cygon
I have to include factory header as there are some convenient methods like Create() and Clone() to do the real job. This object factory is for object hierarchy, so I want to be sure that a developer won't forget to register new object when it is added to the hierarchy.Thanks for selectany! Hope my linker options are good for that. Going to dig MSDN :-)
bocco
+1  A: 

Obviously you just need to move the class registrar declaration into a .cpp file. Perhaps it should be the .cpp file that contains the implementation of the class you register with that registrar.

You don't really need the registrar class, you only need one instance of it. By putting it into a .cpp file you hide it from all the other source completely but still have a global object initialized on startup.

sharptooth
That separates declaration from registration though
Eugene
I don't see a problem in that.
sharptooth
Such class has to be registered in this factory. So, it would be convenient and more safe to do registration simultaneously with declaration.
bocco
I got it. However we've been doing the way I suggest for ages and haven't seen real maintenance problems so far.
sharptooth
OK, I see. It is just a kind of personal taste I guess :-)
bocco
+3  A: 

Cygon's answer is probably what you need, but you also might get away with lazy registration, depending what exacly is registration for.

Move registration into special base class and attach that besides interface in the macro.

Inside constructor use a local static flag, so that each class registers only once on first instanse creation.

Probably won't compile, but you should get what I meant:

template<className, interfaceName>
class RegistratorBase
{
public:
     RegistratorBase()
     {
          static bool registered = false;
          if(!registered)
                ObjectFactory<interfaceName>::GetInstance().Register<className>();
     }
};

#define REGISTER_CLASS(className, interfaceName) \
  class className : public interfaceName, private RegistratorBase<className, interfaceName>
Eugene
Sounds interesting. Thanks
bocco
I really like this solution. I don't think there is even a need for the marco now.
iain
+2  A: 

So you want to put variables definitions in header file? There is a portable way: static variables of template classes. So we get:

template <typename T, typename I>
struct AutoRegister: public I
{
    // a constructor which use ourRegisterer so that it is instantiated; 
    // problem catched by bocco
    AutoRegister() { &ourRegisterer; } 
private:
    static RegisterClass<T, I> ourRegisterer;
};

template <typename T, typename I>
RegisterClass<T, I> AutoRegister<T, I>::ourRegisterer;

class Foo: AutoRegister<Foo, IFoo>
{
public:
    virtual void Do();         
};

Note that I've kept the inheritance of IFoo non virtual. If there is any risk of inheriting from that class multiple times, it should be virtual.

AProgrammer
Cool idea! I completely forgot about that way. But your version won't work as ourRegisterer is never used, so it won't be instantiated. We can add constructor like "AutoRegister() { }" or do explicit instantiation "template struct AutoRegister<Foo, IFoo>;". Please, update your answer if you care :-)
bocco