C++ Abstract Factory using templates

NtscCobalt picture NtscCobalt · Dec 5, 2010 · Viewed 20.4k times · Source

I'm trying to create an abstract factory template for multiple abstract factories in C++ and came up with this.

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <map>
#include <stdio.h>

class Base
{
public:
    virtual ~Base() {}

    virtual bool Get() = 0;
};

class DerivedA : public Base
{
public:
    bool Get()
    {
        return true;
    }
};

class DerivedB : public Base
{
public:
    bool Get()
    {
        return false;
    }
};

template <class T>
class Creator
{
public:
    virtual ~Creator(){}
    virtual T* Create() = 0;
};

template <class T>
class DerivedCreator : public Creator<T>
{
public:
    T* Create()
    {
        return new T;
    }
};

template <class T, class Key>
class Factory
{
public:
    void Register(Key Id, Creator<T>* Fn)
    {
        FunctionMap[Id] = Fn;
    }

    T* Create(Key Id)
    {
        return FunctionMap[Id]->Create();
    }

    ~Factory()
    {
        std::map<Key, Creator<T>*>::iterator i = FunctionMap.begin();
        while (i != FunctionMap.end())
        {
            delete (*i).second;
            ++i;
        }
    }
private:
    std::map<Key, Creator<T>*> FunctionMap;
};

int main(int argc, char** argv[])
{
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

    //Register
    Factory<Base, char*> temp;
    temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA>);
    temp.Register("DB", (Creator<Base>*)new DerivedCreator<DerivedB>);

    //Pointer to base interface
    Base* pBase = 0;

    //Create and call
    pBase = temp.Create("DA");
    printf("DerivedA %u\n", pBase->Get());
    delete pBase;

    //Create and call
    pBase = temp.Create("DB");
    printf("DerivedB %u\n", pBase->Get());
    delete pBase;

 return 0;
}

It compiles and runs fine with no memory leaks (win32 crtdbg) but I don't know if this is really the correct way to do an abstract factory template.

temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA>);

I'm also wondering about the line above. I'm confused why I have to cast. I don't understand templates very well but I'd assume that it should work fine considering that both the template class and the actual class are derived.

That code actually works fine as shown above and even deletes fine with no memory leaks. I just don't feel entirely comfortable with it.

I haven't been able to find any real examples of template classes except for this from MaNGOS (wow emulator) - https://mangos.svn.sourceforge.net/svnroot/mangos/trunk/src/framework/Dynamic/ObjectRegistry.h

But I don't think I can use that method in my project because I plan on using DLLs at some point in my project and it uses CRTP which is against my requirement for runtime polymorphism.

Answer

Pete Kirkham picture Pete Kirkham · Dec 5, 2010

The class DerivedCreator<DerivedA> is a Creator<DerivedA> not a Creator<Base>.

You need to tell the derived template what the base type so it can implement the interface of Creator<Base> by creating an instance of the derived type:

// DerivedCreator is Creator<BaseType> which creates a 
// DerivedType, not a Creator<DerivedType>
template <class DerivedType, class BaseType>
class DerivedCreator : public Creator<BaseType>
{
public:
    BaseType* Create()
    {
        return new DerivedType;
    }
};

// Register
Factory<Base, std::string> temp;
temp.Register("DA", new DerivedCreator<DerivedA, Base>);
temp.Register("DB", new DerivedCreator<DerivedB, Base>);

// or if you want to create lots with the same base:
template <class DerivedType>
class DerivedBaseCreator : public DerivedCreator<DerivedType, Base> {};

//Register
Factory<Base, std::string> temp;
temp.Register("DA", new DerivedBaseCreator<DerivedA>);
temp.Register("DB", new DerivedBaseCreator<DerivedB>);