1

There is a pattern that I like using for implementing factory classes that goes as follows (taken from my answer to this question):

class Factory
{
public:
    template<class DerivedType>
    DerivedType::CreatedType *createType()
    {
        DerivedType::CreatedType *r = (DerivedType::CreatedType) (*(m_creators[DerivedType::id]))();
        return r;
    }
protected:
    static std::map<int,void *(*)()> m_creators;
};

std::map<int,void *(*)()> Factory::m_creators = std::map<int,void*(*)()>();

template<class Derived, class CreatedType>
class CRTPFactory : public Factory
{
    typedef typename CreatedType CreatedType;
public:
    static bool register() 
    {
        Factory::m_creators.push_back(std::make_pair(Derived::id,Derived::create);
        return true;
    }

private:
    static bool m_temp;
};

template<class Derived>
bool CRTPFactory<Derived>::m_temp = CRTPFactory<Derived>::register();

class AFactory : public CRTPFactory<AFactory,A>
{
private:
    static A *create() 
    {
        //do all initialization stuff here
        return new A;
    }

public:
    static const int id = 0;
};

This allows extension of the factory for new types without having to change the factory class. It also allows for implementing specific creation algorithms for different types without having to change the factory class. There is a major problem with this pattern though. The class AFactory is never used explicitly. It registers its creator function at load time through CRTPFactory's member temp. This might be a little complicated to understand but it's very easy to use. The problem is AFactory isn't compiled so it's static parameters aren't initialized at load time. My question is, is it possible to force the compiler (I'm using VS 2012 but answers for GCC are also good) to compile AFactory without ever explicitly creating an instance of it? A solution that I use in VS is to dllexport AFactory, that way the compiler compiles the class even though it doesn't know of anyone instantiating it. This is because it assumes some other dll might instantiate it. The problem with this solution is that the factory class must be implemented in a separate dll as the rest of the code. And also this doesn't work on GCC.

Community
  • 1
  • 1
Benjy Kessler
  • 7,356
  • 6
  • 41
  • 69
  • sorry I renamed lots of variables so it would look nice here. I forgot that one. – Benjy Kessler Jul 04 '13 at 09:20
  • There is another problem with this technique, which is that it requires all specializations of `CRTPFactory` to be instantiated in the same translation-unit (aka cpp file), to guarantee that they are registered after the initialization of `Factory::m_creators`. I would advise using a techique that does not depend on the execution order of the constructors of static members. – willj Jul 08 '13 at 17:02

3 Answers3

3

Inheriting from CRTPFactory<AFactory,A> causes implicit instantiation of the class, but not the definitions of its members.

[temp.inst]

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates;

Instead of inheriting from CRTPFactory<AFactory,A>, you can simply explicitly instantiate the m_temp member.

template bool CRTPFactory<AFactory,A>::m_temp;

For reference, here's the modified example (in a form that compiles):

#include <map>

class Factory
{
public:
    template<class DerivedType, class CreatedType>
    CreatedType *createType()
    {
        CreatedType *r = (CreatedType) (*(m_creators[DerivedType::id]))();
        return r;
    }
protected:
    static std::map<int,void *(*)()> m_creators;
};

std::map<int,void *(*)()> Factory::m_creators = std::map<int,void*(*)()>();

template<class Derived, class CreatedType>
class CRTPFactory : public Factory
{

public:
    static bool register_() 
    {
        Factory::m_creators.insert(std::make_pair(Derived::id,Derived::create));
        return true;
    }

  static bool m_temp;
};

template<class Derived, class CreatedType>
bool CRTPFactory<Derived, CreatedType>::m_temp = CRTPFactory<Derived, CreatedType>::register_();

struct A
{
};

class AFactory
{
public:
    static void *create() 
    {
        //do all initialization stuff here
        return new A;
    }

public:
    static const int id = 0;
};

template bool CRTPFactory<AFactory,A>::m_temp;
willj
  • 2,991
  • 12
  • 24
1

Static class member should be explicitly created somewhere.

Doing something like this in a cpp file of your choice should work:

int AFactory::id = 0
Xaqq
  • 4,308
  • 2
  • 25
  • 38
  • Yeah that's what I do when working in GCC, but I have projects with factories that create tens of classes so I have a CPP file with that line twenty times. This is both very ugly and also it makes the whole design pattern much harder to use because if the person who is writing the factory class is not the same person who is using it you get a lot of confusion. – Benjy Kessler Jul 04 '13 at 09:25
  • Mmm, I get your problem now. Is it mandatory that the `id` field be a static class variable of the factory? Would it be possible to move it into one your factory bases classes? – Xaqq Jul 04 '13 at 09:32
1

You're mistaken in your assumption. class AFactory is definitely compiled. Quite a few times, probably, since it's in a header.

Your real problem is probably that class AFactory is not registered. Why would it be? Which statement would cause it to be? Every statement is ultimately called either from main() or from the initializer of a global variable.

MSalters
  • 173,980
  • 10
  • 155
  • 350