This solution has actually worked out for me, so I'm placing it here as an answer:
PimpleHelper.h is a Pimpl helper class to reduce boiler plate code. Uses the VendorFactory to instantiate the correct vendor implementation. Multiple vendors will register the fact that they implement a given interface; only one vendor's implementation is ever instantiated for a given interface.
#include <QSettings>
#include "VendorFactory.h"
#include <cxxabi.h>
// Pimpl Helper
template<typename T>
class PimplHelper
{
public:
PimplHelper()
{
m_interfaceNameImplemented = demangle(typeid(T).name());
initializeImpl();
}
T* getImpl()
{
return theImpl.data();
}
private:
QScopedPointer< T > theImpl;
QString m_interfaceNameImplemented;
void initializeImpl()
{
// Read in configuration
QSettings settings("AppSettings.ini", QSettings::IniFormat);
QString vendorToUse = settings.value("VENDOR_IMPLEMENTATION_KEY", "Vendor1").toString();
qDebug() << "Vendor to use is: " << vendorToUse << " Interface Implemented: " << m_interfaceNameImplemented;
// Obtain an instance of the vendor's class that implements the T interface
theImpl.reset(
VendorFactory<T>::create(vendorToUse, m_interfaceNameImplemented)
);
if(!theImpl)
qDebug() << "PimplHelper::initializeImpl, error resolving implementation for: "
<< vendorToUse << " Interface Implemented: " << m_interfaceNameImplemented;
}
const QString demangle(const char* name)
{
int status = -4;
char* res = abi::__cxa_demangle(name, NULL, NULL, &status);
const char* const demangled_name = (status==0)?res:name;
QString ret_val(demangled_name);
free(res);
return ret_val;
}
};
VendorFactory.h creates instances of classes implemented by various vendors.
Vendors register their implementations with the factory via a macro.
#include <QtCore>
template< class T>
class VendorFactory
{
private:
typedef T* (*CreateFunc)();
typedef QMap<QString, CreateFunc> FunctionMap;
public:
static T * create(const QString& vendorName, const QString& interfaceName)
{
typename FunctionMap::iterator it = creators()->find(vendorName + interfaceName);
if (it == creators()->end())
return NULL;
return (it.value())();
}
static bool reg(const QString& vendorName, const QString& interfaceName, CreateFunc fun)
{
qDebug() << "Registering: " << vendorName + interfaceName << endl;
creators()->insert(vendorName + interfaceName, fun);
return true;
}
static FunctionMap * creators()
{
static FunctionMap* creators = new FunctionMap;
return creators;
}
virtual ~VendorFactory() {}
};
/// @brief This registers a Vendor's class in the factory and adds a factory function named create_vendorImplClass()
/// and calls VendorFactory::reg() by the help of a dummy static variable to register the function.
/// @param vendorName A string representing the vendor's name
/// @param vendorImplClass The class implementing the interface given by the last parameter
/// @param interface The interface implemented by the vendorImplClass
#define REGISTER_IN_FACTORY( vendorName, vendorImplClass, interface ) \
namespace { \
interface* create_ ## vendorImplClass() { return new vendorImplClass; } \
static bool vendorImplClass ## _creator_registered = VendorFactory< interface >::reg( vendorName, # interface, create_ ## vendorImplClass); }
And here is how they are used:
Person.h (public-facing API)
#include "IPerson.h"
#include "PimplHelper.h"
// Public facing API
class Person: public IPerson
{
public:
Person()
{
impl.reset( new PimplHelper<IPerson>());
}
QString GetFirstName();
QString GetLastName();
private:
QScopedPointer< PimplHelper<IPerson> > impl;
};
Person.cpp (public-facing API)
#include "Person.h"
QString Person::GetFirstName()
{ // I'd like to remove the call to getImpl() here
// and just use the overloaded -> operator, but it
// gives me a "has no member named GetFirstName()" error
return impl->getImpl()->GetFirstName();
}
QString Person::GetLastName()
{
return impl->getImpl()->GetLastName();
}
PersonImpl1.h contains Vendor1's implementation
#include "IPerson.h"
#include "VendorFactory.h"
// Private Implementation
class PersonImpl1: public IPerson
{
public:
PersonImpl1():
FirstName("Jon"), LastName("Skeet")
{}
QString GetFirstName()
{
return FirstName;
}
QString GetLastName()
{
return LastName;
}
private:
QString FirstName;
QString LastName;
};
REGISTER_IN_FACTORY("Vendor1", PersonImpl1, IPerson)
PersonImpl2.h contains Vendor2's implementation
#include "IPerson.h"
#include "VendorFactory.h"
// Private Implementation
class PersonImpl2: public IPerson
{
public:
PersonImpl2(): FirstName("Chuck"), LastName("Norris")
{}
QString GetFirstName()
{
return FirstName;
}
QString GetLastName()
{
return LastName;
}
private:
QString FirstName;
QString LastName;
};
REGISTER_IN_FACTORY("Vendor2", PersonImpl2, IPerson)
Finally, the main.cpp file:
#include <QCoreApplication>
#include <QDebug>
#include "Person.h"
// The following needs to be included for the static/auto registration
// with the VendorFactory to occur. I'm not exactly sure why.
#include "PersonImpl1.h"
#include "PersonImpl2.h"
int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
Person* p = new Person();
qDebug() << "The person implemented is: "
<< p->GetFirstName() << " " << p->GetLastName();
qDebug() << "exiting";
}
Here is a list of other SO questions that helped me so far:
Instantiate class from name?
Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates
Register an object creator in object factory
Entity/Component Systems in C++, How do I discover types and construct components?
Is there a way to instantiate objects from a string holding their class name?
Static variable not initialized
Unmangling the result of std::type_info::name