What are good strategies for selecting, either at run-time or compile
time, various pimpl'ed implementations? While retaining the ability
to switch implementations without recompiling.
Boost has an example but with only one implementation class: (what
would an example with 2 implementation classes look like?)
http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/sp_techniques.html#pimpl
The pimpl'ed class cpp file has to include at least one implementation
header file. Is there a method to avoid changing that included header
when switching implementations?
Or, are we using the pimpl idiom incorrectly?
thanks,
graham
Funny you asked, as I just finished doing exactly this for a project I'm
working on. Here's what I did:
// Factory.h
#include <map>
#include <string>
template <typename T>
class Factory
{
public:
typedef std::map<std::string, typename T::constructor_type> Map;
static Factory<T>* getFactory()
{
if ( ! m_instance )
m_instance = new Factory<T>;
return m_instance;
}
typename T::constructor_type createKind( std::string output_kind )
{
typename Map::iterator it;
if if ( ( it = constructorMap.find( output_kind ) ) != constructorMap.end() )
return (*it).second;
// Throw something here
}
void void registerKind( std::string kind, typename T::constructor_type
constructor) {
constructorMap[kind] = constructor;
}
private:
Map constructorMap;
static Factory<T>* m_instance;
};
// FooImpl.h
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include "Factory.h"
FooImpl
{
public:
typedef typedef boost::function<boost::shared_ptr<FooImpl>()>
constructor_type; static Factory<FooImpl> factory;
};
// FooImpl.cpp
Factory<FooImpl> FooImpl::factory;
// FooFake.cpp
#include "FooImpl.h"
class FooFake :
public FooImpl
{
private:
struct FooFakeCreator
{
FooFakeCreator oFakeCreator () {
FooImpl::factory.getFactory()->registerKind("Fake", FooFakeCreator::create );
static static boost::shared_ptr<FooImpl> create () { return
boost::shared_ptr<FooImpl>( new FooFake() ); } }
static FooFakeCreator m_creator;
};
FooFake::FooFakeCreator FooFake::m_creator;
You could probably clean this up, but it allows your users to be agnostic to
the different implementations. I didn't include the actual Foo object, that
in my case contains a weak pointer to a FooImpl. My implementation also
requires the caller to FooImpl::factory->createKind(string) to provide the
type, but this could be read in from a config file and you could remove this
requirement. Have some sort of factory registry that createKind would look
into.
This may or may not have a "pattern name", let me know someone if it does.