Reputation: 7616
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.
Upvotes: 1
Views: 444
Reputation: 2999
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;
Upvotes: 3
Reputation: 179819
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.
Upvotes: 1
Reputation: 4386
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
Upvotes: 1