Inspired by answer Johannes Schaub’s answer I tried to implement a reflective factory. The idea is that you can create objects by passing the name of the class to a method which creates the concerning object on the heap and returns a pointer to a common base class. Using dynamic_cast and RTTI the objects can be casted back to their original type if necessary. One should be able to use the reflective factory as shown below:
// Base is the base class of all objects to create.
class Factory: public AbstractFactory<Base>
{
public:
Factory()
{
m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;
m_map["DerivedB"] = &AbstractFactory::createInstance<DerivedB>;
}
};
int main()
{
Factory factory;
Base *a = factory.Create("DerivedA");
DerivedA *aa = dynamic_cast<DerivedA*>(a);
// etc..
}
I got this working so far. However there are two main problems I have with the code below. It is ugly and if I make the methods of the abstract factory protected it complaints that it cannot access the createInstance() method. This is something I do not understand. By definition the derived class should be able to access the protected methods of the base class. I tested the code in VS 2008. Further notes: I know the base class is not really abstract, because it does not contain a pure virtual function. I know about std::function however so far I haven’t used it. Maybe I’ll do in the future.
#include <iostream>
#include <map>
#include <typeinfo>
#include <string>
struct Base
{
virtual std::string SayHello() = 0;
};
struct DerivedA: public Base
{
virtual std::string SayHello()
{
return "Hello from DerivedA";
}
};
struct DerivedB: public Base
{
virtual std::string SayHello()
{
return "Hello form DerivedB";
}
};
/**
* @brief Reflective Factory class. Creates objects of classes which derive from
* a common base class.
*
*/
template<class BASE_T>
struct AbstractFactory
{
// Macro to call ptrs to member functions as recommended in the C++ FAQ lite.
// http://www.parashift.com/c++-faq-lite/pointers-to-members.html
#define CALL_MEMBER_FN(object, ptrToMember) ((object).*(ptrToMember))
// Recall funcion ptr syntax for members: ReturnType (class::*) (Arguments)
// using a typedef makes it easier..
typedef BASE_T* (AbstractFactory::*func_ptr_type) ();
// retType^ ClassName^ AliasName^ Arguments^
typedef std::map<std::string, func_ptr_type> map_type;
template<typename DERIVED_T>
BASE_T * createInstance()
{ return new DERIVED_T; }
map_type m_map;
/**
* @brief Creates an object from a class with the name given as string.
*/
BASE_T * Create(std::string className)
{
// Note the last () at the end.
return CALL_MEMBER_FN(*this, m_map[className])();
}
#undef CALL_MEMBER_FN
};
class Factory: public AbstractFactory<Base>
{
public:
Factory()
{
m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;
m_map["DerivedB"] = &AbstractFactory::createInstance<DerivedB>;
}
};
int main()
{
Factory factory;
Base *a = factory.Create("DerivedA");
DerivedA *aa = dynamic_cast<DerivedA*>(a);
std::cout << typeid(a).name() << std::endl;
std::cout << typeid(*a).name() << std::endl;
std::cout << typeid(aa).name() << std::endl;
std::cout << aa->SayHello() << std::endl;
std::cin.get();
return 0;
}
Update
The exact error I get using VS 2008 is (in German, sorry was not my choice..)
1>------ Erstellen gestartet: Projekt: ReflectiveFactory, Konfiguration: Debug Win32 ------
1>Kompilieren...
1>main.cpp
1>.\main.cpp(82) : error C2248: "AbstractFactory<BASE_T>::createInstance": Kein Zugriff auf protected Member, dessen Deklaration in der AbstractFactory<BASE_T>-Klasse erfolgte.
1> with
1> [
1> BASE_T=Base
1> ]
1> .\main.cpp(55): Siehe Deklaration von 'AbstractFactory<BASE_T>::createInstance'
1> with
1> [
1> BASE_T=Base
1> ]
1>.\main.cpp(83) : error C2248: "AbstractFactory<BASE_T>::createInstance": Kein Zugriff auf protected Member, dessen Deklaration in der AbstractFactory<BASE_T>-Klasse erfolgte.
1> with
1> [
1> BASE_T=Base
1> ]
1> .\main.cpp(55): Siehe Deklaration von 'AbstractFactory<BASE_T>::createInstance'
1> with
1> [
1> BASE_T=Base
1> ]
The
Createmethod must be accessible to end users, which means that either that method ispublicinAbstractFactoryor it is moved toFactoryand made public there. The rest of the code inAbstractFactorycan be protected.It seems that only gcc accepts the code (clang++, comeau reject it) and it could be rightfully rejected (I would have to look into the standard). At any rate a simple workaround would be to create a helper function that will provide the member function pointer for you:
This template member method of the
AbstractFactorytemplate can be protected, as you are directly calling it on your own base in the actualFactory:After checking with the standard:
11.5p1 [class.protected]
That is, the expression
m_map["DerivedA"] = &AbstractFactory::createInstance<DerivedA>;is incorrect, whilem_map["DerivedA"] = &Factory::createInstance<DerivedA>;is correct (regarding access specifiers, but not types, the left hand side is anB* (AbstractFactory<B>::*)( std::string )and the right hand side is aB* (Factory::*)( std::string ), so the assignment would fail.This plays nice with the semantics of
protectedeverywhere else, and in particular with not being able to access the protected members of other than your own base subobject: