C++ Automatic Factory Registration of Derived Types

c++ automatic factory registration of derived types

I use a singleton with a member for registration, basically:

template< typename KeyType, typename ProductCreatorType >
class Factory
{
typedef boost::unordered_map< KeyType, ProductCreatorType > CreatorMap;
...
};

Using Loki I then have something along these lines:

 typedef Loki::SingletonHolder< Factory< StringHash, boost::function< boost::shared_ptr< SomeBase >( const SomeSource& ) > >, Loki::CreateStatic > SomeFactory;

Registration is usually done using a macro such as:

#define REGISTER_SOME_FACTORY( type ) static bool BOOST_PP_CAT( type, __regged ) = SomeFactory::Instance().RegisterCreator( BOOST_PP_STRINGIZE( type ), boost::bind( &boost::make_shared< type >, _1 ) );

This setup has a number of advantages:

  • Works with for example boost::shared_ptr<>.
  • Does not require maintaining a huge file for all the registration needs.
  • Is very flexible with the creator, anything goes pretty much.
  • The macro covers the most common use case, while leaving the door open for alternatives.

Invoking the macro in the .cpp file is then enough to get the type registered at start up during static initialization. This works dandy save for when the type registration is a part of a static library, in which case it won't be included in your binary. The only solutions which compiles the registration as a part of the library which I've seen work is to have one huge file that does the registration explicitly as a part of some sort of initialization routine. Instead what I do nowadays is to have a client folder with my lib which the user includes as a part of the binary build.

From your list of requirements I believe this satisfies everything save for using a registrator class.

Automatic registration of derived classes in a factory pattern

Your code looks too complicated to me. Maybe there is a reason that I miss.

You cannot avoid the registration. What you are looking for is registration at compile time, which I think it is not possible. Since you are looking for registration at compile time, it means that you do not need to expose the registration method in interface. So, your client interface should be made of the base class and a creation method:

// { client interface header
class B
{
public:
virtual ~B() = 0 {}
//...
};

B* Create( const char* s );
// } client interface header

A simple creation function:

// { client interface implementation

template< typename T >
B* New()
{
return new T;
}

typedef B* (*PFNew)();

B* Create( const char* s )
{
typedef std::map< string, PFNew > Map;

static Map m;
if ( ! m.size() )
{
m[ D1::Signature() ] = New< D1 >;
m[ D2::Signature() ] = New< D2 >;
//...
}

Map::const_iterator ci = m.find( s );
if ( ci == m.end() )
throw -1;
return ci->second();
}

// } client interface implementation

Register constructors in a factory automatically

You can create a static constructor for factory and inside this constructor can find all sub-classes of Dataset (DataSetSeries, DataSetTable, ...). and call static constructor for them.

public static class Factory
{
static Factory()
{
var datasetDerrivedTypes = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => typeof(DataSet).IsAssignableFrom(t) &&
t != typeof(DataSet));

foreach (var type in datasetDerrivedTypes)
{
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.TypeHandle);
}
}

public static void Register(Type type, Func<Series, DataSet> constructorDelegate)
{

}
}

C++ Library & Self registering classes: Factory map empty in client application

From the blog post, you are missing the static member registered (also called "// The really fun part"). Having and instantiating such a static variable in the base class forces it to be instantiated in all derived classes and this will register the class as a side effect.

EDIT: There is another very small but very important piece of code in the blog post:

    Registrar() : Base(Key{}) { (void)registered; }

This will ensure that registered is used. Because a static variable is only instantiated the first time it is used, otherwise the function is not called.

In your case, adding the following to node_template should work:

template <class derived>
struct node_template :
node,
factory::registrar<derived>
{
node_template(const std::string& uuid_string) :
node(uuid_string),
factory::registrar<derived>(uuid_string)
{
(void) registered;
}

static bool do_register() {
derived d; // I am not sure if one should in some way force this to not be optimized away.
return true;
}

inline static bool registered = do_register();
};

Enforce Registration of Derived Types

You can cause a linker error in g++ at least by adding a dummy function to the registrar: void blah() { } and then calling it from a dummy constructor in the CRTP base class:

public:
Base() { reg.blah(); }

I can't see a more elegant way to solve this problem.



Related Topics



Leave a reply



Submit