views:

143

answers:

5

I have an abstract base class like so:

class AbstractBaseClass
{}; 

a templated concrete class that derives from it:

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};

AndI have a factory class that creates AbstractBaseClasses

class MyFactory
{
public:
   boost::shared_ptr<AbstractBaseClass> CreateBlah();
   boost::shared_ptr<AbstractBaseClass> CreateFoo();

   template<class T>
   boost::shared_ptr<AbstractBaseClass> Create(T input)
   {
      return boost::shared_ptr<AbstractBaseClass>(new ConcreteClass<T>(input));
   }
};

The problem with this, is that now EVERYTHING that uses MyFactory has to include the entire implementation to ConcreteClass. Ideally, I want nothing but MyFactory to know about ConcreteClass.

Is there any way to architect this to achieve this goal? (Besides manually making a new Create function in MyFactory for every type I want instead of templating it).

A: 

You're looking for the "PIMPL" idiom. There's a good explanation at Herb Sutter's GOTW site

Harper Shelby
Im not sure how PImpl would help me here. Since I am dealing with Templates, the implementation is needed at compile time. Since the Factory's create method is templated, it would need to call the PImpl's function from the header file. If I am missing something, could you please be more specific?
Lima Beans
It would reduce the dependency to the interface, but not completely remove it. To get completely free, you'll need to use a registry-type approach, where the Factory doesn't know what it's loading until runtime. This kind of thing is common for plugin architectures, where the Factory reads a registry to find out how to create something. It's a bit hairier, but pretty cool when you get it working.
Harper Shelby
@Harper, you would lose compiler static check if your implementation depends on something not written in source code.
YeenFei
@YeenFei: Not completely - plugin architectures in C++ have both the plugin and plugged-into depend on a common header, though they're not compiled together. While this does leave room for human error, as long as both compilations use the same file, you have static compile-time checking of the code. Your registration system, if dependent on configuration files, could have run-time only errors, but all of your code is still checked correctly.
Harper Shelby
A: 

It can't be done because ConcreteClass is a template, that means you need the complete implementation available at compile time. Same reason why you can't precompile templates and have to write them all in header files instead.

Aleph
you can - via explicit template instanciation
David Feurle
+4  A: 

you'll need to put the factory implementation into the implementation file (which you mentioned you'd like to avoid, but it is the lesser evil unless the interfaces are small, and/or your projects are small).

of course, there are a few other ways you could approach this, such as putting the implementation into base classes, and making derived bases factories, or using some other really weird template syntax to reduce instantiations in dependent translations. this really comes down to convenience and scale for your project. if you are working on one or more large projects, then full abstraction wrt instantiation will serve your needs best in the long run (assuming you need dynamic polymorphism and memory).

you may also try other approaches (such as overloading) to reduce errors by using type-safety.

the short answer is that you'll really need to abstract the interfaces/instantiation into one or multiple implementation files to remove header dependencies - very common idiom, and many ways to tackle it. you can over course further divide and use polymorphism for your factories as well.

you may also use template forward declarations to minimize the sets to the compilation unit. provided:

/** in MyIntermediateFactory.hpp */
class MyIntermediateFactory {
public:
    static template<class T> boost::shared_ptr<T> Create(float);
};

/** in Concrete.hpp */
template<Concrete>
boost::shared_ptr<T> MyIntermediateFactory::Create<Concrete>(float arg) {
    /* … */
}

using this you can select portions of programs/interfaces which you need in the library, then wrap it all up in a real Factory (for the build at hand). the linker/instantiation should fail along the way if you actually attempt to request a creation which is not visible.

there a lot of options, really - you need to figure out how big your scale is in order to determine what to abstract (or not). instantiation requires interface, to remove header dependencies, you'll have to abstract the instantiation someplace.

Justin
+1  A: 

You could use explicit template instanciation. Trying to call the factory method with a template parameter not explicit instanciated will give you a linker error. Note the explicit template instanciation in MyFactory.cpp

template AbstractBaseClass* MyFactory::Create(int input);

All put together looks like this (I removed shared_ptr for the sake of simplicity):

Main.cpp:

#include "AbstractBaseClass.h"
#include "MyFactory.h"

//we do not need to know nothing about concreteclass (neither MyFactory.h includes it)    

int main()
{
    MyFactory f;
    AbstractBaseClass* ab = f.Create(10);
    ab = f.Create(10.0f);
    return 0;
}

MyFactory.h:

#include "AbstractBaseClass.h"

class MyFactory
{
public:

   template<class T>
   AbstractBaseClass* Create(T input);
 };

MyFactory.cpp:

#include "MyFactory.h"
#include "ConcreteClass.h"

template<class T>
AbstractBaseClass* MyFactory::Create(T input) {
    return new ConcreteClass<T>(input);
}

//explicit template instanciation
template AbstractBaseClass* MyFactory::Create(int input);

//you could use as well specialisation for certain types
template<>
AbstractBaseClass* MyFactory::Create(float input) {
    return new ConcreteClass<float>(input);
}

AbstractBaseClass.h:

class AbstractBaseClass{};

ConcreteClass.h:

#include "AbstractBaseClass.h"

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};
David Feurle
interesting, didnt know I could do this.
Lima Beans
@lima doesnt this work for your problem?
David Feurle
unfortunately, there aren't any compilers (except Comeau's one) that can compile MyFactory.h. You simply can't declare a templated function and define it in another file. It is, indeed, in the standard, but nobody supports this feature.
Grim Fandango
@grim the code compiles (and works) with gcc-4.4 will check this with visual studio...
David Feurle
@grim works as well on visual studio
David Feurle
@grim see that: http://www.codeguru.com/cpp/com-tech/atl/tutorials/article.php/c3617 - it states that explicit template instanciation wasn't well supported by Visual Studio 6
David Feurle
+1  A: 

My approach to the same problem in the past was the creation of a set of concrete factories (one per type) that get registered in the global factory (for illustration purposes, indexing by object name):

class AbstractBaseClass;
class ConcreteFactory
{
public:
   AbstractBaseClass * create();
};
class AbstractFactory 
{
public:
   void registerFactory( std::string const & name, std::shared_ptr<ConcreteFactory> const & f )
   {
      factory[ name ] = f; // check for collisions, complain if so ...
   }
   AbstractBaseClass * create( std::string const & name )
   {
      return factory[name]->create(); // check for existence before dereferencing...
   }
private:
   std::map<std::string, std::shared_ptr<ConcreteFactory> > factory;
};

I used this in a piece of code that was heavily templated to reduce compilation time. Each concrete factory and the class that it creates need only be in a single translation unit that registers the concrete factory. The rest of the code only need to use the common interface to AbstractBaseClass.

David Rodríguez - dribeas