Dynamically Register Constructor Methods in an Abstractfactory at Compile Time Using C++ Templates

Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates

Answer One

The general technique of deriving a class like this is the Curiously Recurring Template Pattern (CRTP):

class PingMessage: public MessageTmpl < 10, PingMessage > 

Your specific technique of using a template class's static member initialization to register subclasses of that class is (IMO) simply brilliant, and I've never seen that before. A more common approach, used by unit test frameworks like UnitTest++ and Google Test, is to provide macros that declare both a class and a separate static variable initializing that class.

Answer Two

Static variables are initialized in the order listed. If you move your m_List declaration before your MessageFactory::Register calls, you should be safe. Also keep in mind that if you start declaring Message subclasses in more than one file, you'll have to wrap m_List as a singleton and check that it's initialized before each use, due to the C++ static initialization order fiasco.

Answer Three

C++ compilers will only instantiate template members that are actually used. Static members of template classes is not an area of C++ that I've used much, so I could be wrong here, but it looks like providing the constructor is enough to make the compiler think that MESSAGE_ID is used (thus ensuring that MessageFactory::Register is called).

This seems very unintuitive to me, so it may be a compiler bug. (I was testing this in g++ 4.3.2; I'm curious to know how Comeau C++, for example, handles it.)

Explicitly instantiating MESSAGE_ID also suffices, at least in g++ 4.3.2:

template const uint16_t PingMessage::MESSAGE_ID;

But that's even more unnecessary work than providing an empty default constructor.

I can't think of a good solution using your current approach; I'd personally be tempted to switch to a technique (such as macros or using a script to generate part of your source files) that relied less on advanced C++. (A script would have the added advantage of easing maintenance of MESSAGE_IDs.)

In response to your comments:

Singletons are generally to be avoided because they're often overused as poorly disguised global variables. There are a few times, however, when you really do need a global variable, and a global registry of available Message subclasses is one of those times.

Yes, the code that you provided is initializing MESSAGE_ID, but I was talking about explicitly instantiating each subclass's instance of MESSAGE_ID. Explicit instantiation refers to instructing the compiler to instantiate a template even if it thinks that that template instance won't otherwise be used.

I suspect that the static function with the volatile assignment is there to trick or force the compiler into generating the MESSAGE_ID assignment (to get around the problems that dash-tom-bang and I pointed out with the compiler or linker dropping or not instantiating the assignment).

Factory method implementation - C++

In the example you posted, neither a factory or a template approach makes sense to me.
My solution involves a data member in the Pen class.

class Pen {
public:
Pen() : m_color(0,0,0,0) /* the default colour is black */
{
}

Pen(const Color& c) : m_color(c)
{
}

Pen(const Pen& other) : m_color(other.color())
{
}

virtual void Draw()
{
cout << "Drawing with a pen of color " << m_color.hex();
}
void setColor(const Color& c) { m_color = c; }
const Color& color() const { return m_color; }
private:
Color m_color;
};

class Color {
public:
Color(int r, int g, int b, int a = 0) :
m_red(r), m_green(g), m_blue(other.blue()), m_alpha(a)
{
}

Color(const Color& other) :
m_red(other.red()), m_green(other.green()),
m_blue(other.blue()), m_alpha(other.alpha())
{
}

int red() const { return m_red; }
int green() const { return m_green; }
int blue() const { return m_blue; }
int alpha() const { return m_alpha; }

std::string hex() const
{
std::ostringstream os;
char buf[3];
os << "#";

sprintf(buf, "%2X", red());
os << buf;

sprintf(buf, "%2X", green());
os << buf;

sprintf(buf, "%2X", blue());
os << buf;

sprintf(buf, "%2X", alpha());
os << buf;

return os.str();
}

private:
int m_red;
int m_green;
int m_blue;
int m_alpha;
}

Of course, the color class would have to be adjusted to the drawing API you use -- and perhaps be way more advanced than this one (different color spaces, etc).

Why not templates?

The reason it does not make sense to use templates, is that (presumably) the only difference between the different drawing operations is the color variable. So, by using templates (or manually declaring different classes, as you did), you will duplicate similar code. This will make your program large, and slow it down.

So, the draw function should either take the color as an argument, or (as in my example) have the color as a class data member.

Alternative to virtual templates

The following variation will work for at least some purposes consistent with the information you've given so far.

class Generator {
public:
template< size_t SIZE >
Data<SIZE> generate() {
Data<SIZE> x;
vgenerate(SIZE, x.pointer_to_internals());
return x;
}
protected:
virtual void vgenerate(size_t size, InternalDataPointer *p) = 0;
};

class PseudoRandomX : public Generator {
void vgenerate(size_t size, InternalDataPointer *p) override {...}
};

Another solution that will work for a different set of purposes is

template< size_t SIZE >
class Generator {
public:
virtual Data<SIZE> generate() = 0;
};

template< size_t SIZE >
class PseudoRandomX : public Generator<SIZE> {
Data<SIZE> generate() override {...}
};

Is it possible to make a factory in C++ that complies with the open/closed principle?

I think that the open/closed approach and DRY are good principles. But they are not sacred. The goal should be making the code reliable and maintainable. If you have to perform unnatural acts to adhere to O/C or DRY, then you may simply be making your code needlessly more complex with no material benefit.

Here is something I wrote a few years ago on how I make these judgment calls.

Prevent two classes from inheriting from a base class with same template arguments

I think that is impossible.

If it were possible, then we can make compiler to generate error for the following code as well, which is conceptually equivalent to your code.

struct Base {};
struct OtherBase {};

struct A : Base {}; //Base is used here!
struct B : Base {}; // error - used base class. please use some other base!
struct C : OtherBase {}; // ok - unused based!

Elegant way to implement extensible factories in C++

If I understand this correctly, we want a factory function that can select which derived class to instantiate based on constructor inputs. This is the most generic solution that I could come up with so far. You specify mapping inputs to organize factory functions, and then you can specify constructor inputs upon factory invocation. I hate to say that the code explains more than I could in words, however I think the example implementations of FactoryGen.h in Base.h and Derived.h are clear enough with the help of comments. I can provide more details if necessary.

FactoryGen.h

#pragma once

#include <map>
#include <tuple>
#include <typeinfo>

//C++11 typename aliasing, doesn't work in visual studio though...
/*
template<typename Base>
using FactoryGen<Base> = FactoryGen<Base,void>;
*/

//Assign unique ids to all classes within this map. Better than typeid(class).hash_code() since there is no computation during run-time.
size_t __CLASS_UID = 0;

template<typename T>
inline size_t __GET_CLASS_UID(){
static const size_t id = __CLASS_UID++;
return id;
}

//These are the common code snippets from the factories and their specializations.
template<typename Base>
struct FactoryGenCommon{
typedef std::pair<void*,size_t> Factory; //A factory is a function pointer and its unique type identifier

//Generates the function pointer type so that I don't have stupid looking typedefs everywhere
template<typename... InArgs>
struct FPInfo{ //stands for "Function Pointer Information"
typedef Base* (*Type)(InArgs...);
};

//Check to see if a Factory is not null and matches it's signature (helps make sure a factory actually takes the specified inputs)
template<typename... InArgs>
static bool isValid(const Factory& factory){
auto maker = factory.first;
if(maker==nullptr) return false;

//we have to check if the Factory will take those inArgs
auto type = factory.second;
auto intype = __GET_CLASS_UID<FPInfo<InArgs...>>();
if(intype != type) return false;

return true;
}
};

//template inputs are the Base type for which the factory returns, and the Args... that will determine how the function pointers are indexed.
template<typename Base, typename... Args>
struct FactoryGen : FactoryGenCommon<Base>{
typedef std::tuple<Args...> Tuple;
typedef std::map<Tuple,Factory> Map; //the Args... are keys to a map of function pointers

inline static Map& get(){
static Map factoryMap;
return factoryMap;
}

template<typename... InArgs>
static void add(void* factory, const Args&... args){
Tuple selTuple = std::make_tuple(args...); //selTuple means Selecting Tuple. This Tuple is the key to the map that gives us a function pointer
get()[selTuple] = Factory(factory,__GET_CLASS_UID<FPInfo<InArgs...>>());
}

template<typename... InArgs>
static Base* make(const Args&... args, const InArgs&... inArgs){
Factory factory = get()[std::make_tuple(args...)];
if(!isValid<InArgs...>(factory)) return nullptr;
return ((FPInfo<InArgs...>::Type)factory.first) (inArgs...);
}
};

//Specialize for factories with no selection mapping
template<typename Base>
struct FactoryGen<Base,void> : FactoryGenCommon<Base>{
inline static Factory& get(){
static Factory factory;
return factory;
}

template<typename... InArgs>
static void add(void* factory){
get() = Factory(factory,__GET_CLASS_UID<FPInfo<InArgs...>>());
}

template<typename... InArgs>
static Base* make(const InArgs&... inArgs){
Factory factory = get();
if(!isValid<InArgs...>(factory)) return nullptr;
return ((FPInfo<InArgs...>::Type)factory.first) (inArgs...);
}
};

//this calls the function "initialize()" function to register each class ONCE with the respective factory (even if a class tries to initialize multiple times)
//this step can probably be circumvented, but I'm not totally sure how
template <class T>
class RegisterInit {
int& count(void) { static int x = 0; return x; } //counts the number of callers per derived
public:
RegisterInit(void) {
if ((count())++ == 0) { //only initialize on the first caller of that class T
T::initialize();
}
}
};

Base.h

#pragma once

#include <map>
#include <string>
#include <iostream>
#include "Procedure.h"
#include "FactoryGen.h"

class Base {
public:
static Base* makeBase(){ return new Base; }
static void initialize(){ FactoryGen<Base,void>::add(Base::makeBase); } //we want this to be the default mapping, specify that it takes void inputs

virtual void speak(){ std::cout << "Base" << std::endl; }
};

RegisterInit<Base> __Base; //calls initialize for Base

Derived.h

#pragma once

#include "Base.h"

class Derived0 : public Base {
private:
std::string speakStr;
public:
Derived0(std::string sayThis){ speakStr=sayThis; }

static Base* make(std::string sayThis){ return new Derived0(sayThis); }
static void initialize(){ FactoryGen<Base,int>::add<std::string>(Derived0::make,0); } //we map to this subclass via int with 0, but specify that it takes a string input

virtual void speak(){ std::cout << speakStr << std::endl; }
};

RegisterInit<Derived0> __d0init; //calls initialize() for Derived0

class Derived1 : public Base {
private:
std::string speakStr;
public:
Derived1(std::string sayThis){ speakStr=sayThis; }

static Base* make(std::string sayThat){ return new Derived0(sayThat); }
static void initialize(){ FactoryGen<Base,int>::add<std::string>(Derived0::make,1); } //we map to this subclass via int with 1, but specify that it takes a string input

virtual void speak(){ std::cout << speakStr << std::endl; }
};

RegisterInit<Derived1> __d1init; //calls initialize() for Derived1

Main.cpp

#include <windows.h> //for Sleep()
#include "Base.h"
#include "Derived.h"

using namespace std;

int main(){
Base* b = FactoryGen<Base,void>::make(); //no mapping, no inputs
Base* d0 = FactoryGen<Base,int>::make<string>(0,"Derived0"); //int mapping, string input
Base* d1 = FactoryGen<Base,int>::make<string>(1,"I am Derived1"); //int mapping, string input

b->speak();
d0->speak();
d1->speak();

cout << "Size of Base: " << sizeof(Base) << endl;
cout << "Size of Derived0: " << sizeof(Derived0) << endl;

Sleep(3000); //Windows & Visual Studio, sry
}

I think this is a pretty flexible/extensible factory library. While the code for it is not very intuitive, I think using it is fairly simple. Of course, my view is biased seeing as I'm the one that wrote it, so please let me know if it is the contrary.

EDIT : Cleaned up the FactoryGen.h file. This is probably my last update, however this has been a fun exercise.

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.



Related Topics



Leave a reply



Submit