Elegant Way to Implement Extensible Factories in C++

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.

How to design a simple C++ object factory?

I think there are two separate problems here.

One problem is: how does TheManager name the class that it has to create? It must keep some kind of pointer to "a way to create the class". Possible solutions are:

  • keeping a separate pointer for each kind of class, with a way to set it, but you already said that you don't like this as it violates the DRY principle
  • keeping some sort of table where the key is an enum or a string; in this case the setter is a single function with parameters (of course if the key is an enum you can use a vector instead of a map)

The other problem is: what is this "way to create a class"? Unfortunately we can't store pointers to constructors directly, but we can:

  • create, as others have pointed out, a factory for each class
  • just add a static "create" function for each class; if they keep a consistent signature, you can just use their pointers to functions

Templates can help in avoiding unnecessary code duplication in both cases.

Building an encapsulated but extensible animation library in c++

My own solution, a variant on the type-erasure method proposed by Yakk. More details on the problem and this specific approach can be found here.

struct image{};

struct renderable_concept {
virtual image render() const = 0;
};

template <class WRAPPED, class RENDERER>
struct renderable_model : public renderable_concept {
WRAPPED *w;
RENDERER r;
virtual image render() const final override {
return r.render(*w);
}
renderable_model(WRAPPED *w_, RENDERER r_) : w(w_), r(r_) {}
};

struct node {
template <class WRAPPED, class RENDERER>
node(WRAPPED *w_, RENDERER r_) :
p_renderable(new renderable_model<WRAPPED,RENDERER>(w_,r_)) {}

template <class RENDERER>
node(RENDERER r_) : node(this,r_) {}

image render() {return p_renderable->render();}
vector<shared_ptr<node>> children;
unique_ptr<renderable_concept> p_renderable;
};

struct text_node : public node {
template<class RENDERER>
text_node(RENDERER r) : node(this,r) {}

string val;
};

struct shape_node : public node {
template<class RENDERER>
shape_node(RENDERER r) : node(this,r) {}
};

struct color_renderer {
image render(node &) const {/*implementation*/};
image render(text_node &) const {/*implementation*/};
image render(shape_node &) const {/*implementation*/};
};

struct grayscale_renderer {
image render(node &) const {/*implementation*/};
image render(text_node &) const {/*implementation*/};
image render(shape_node &) const {/*implementation*/};
};

C automatic-expandable array of pointers

You can avoid void* with something like this:

#include <stdio.h>
#include <stdlib.h>

#define List(T) \
typedef struct { \
T** items; \
int count; \
} List_ ## T ;\
\
List_ ## T * List_ ## T ## _New() { \
List_ ## T * list = (List_ ## T *) malloc(sizeof(List_ ## T)); \
list->count = 0; \
return list; \
} \
\
void List_ ## T ## _Add(List_ ## T *list, T * data) { \
printf("%d\n", ++list->count); \
} \
void List_ ## T ## _Del(List_ ## T *list, int index) { \
printf("%d\n", --list->count); \
}

/* define just one list per type */
List(int);
List(double);

int main()
{
int a, b, c;
double d, e;
List_int *l1;
List_double *l2;

l1 = List_int_New();
List_int_Add(l1, &a);
List_int_Add(l1, &b);
List_int_Add(l1, &c);
List_int_Del(l1, 0);
List_int_Del(l1, 0);
List_int_Del(l1, 0);
free(l1);

l2 = List_double_New();
List_double_Add(l2, &d);
List_double_Add(l2, &e);
List_double_Del(l2, 0);
List_double_Del(l2, 0);
free(l2);

return 0;
}

That's a poor man's template =)

Elegant solution to class design

OK I found the answer.
The dynamic keyword is the clue here.
We can write:

void Handle(C c)
{
dynamic cc = c;
HandleSpecific(cc);
}
void HandleSpecific(A a)
{
//Specific behavior A
}
void HandleSpecific(B b)
{
//Specific behavior B
}

Drawbacks are of course - Risk of exception beacause of runtime binding introduced here and slight performance hit.



Related Topics



Leave a reply



Submit