Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Abstract Factory using templates

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.

like image 227
NtscCobalt Avatar asked Dec 05 '10 05:12

NtscCobalt


2 Answers

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>);
like image 152
Pete Kirkham Avatar answered Sep 27 '22 20:09

Pete Kirkham


Small remarks to improve the design : 1) Use shared_ptr instead of raw pointers 2) use std::string instead of char*

You have to cast, because types Creator, Creator and Creator< DerivedB > are completely different types. The way to fix that is to remove casts :

//Register
Factory<Base, char*> temp;
temp.Register("DA", new DerivedCreator<Base>);
temp.Register("DB", new DerivedCreator<Base>);
like image 26
BЈовић Avatar answered Sep 27 '22 19:09

BЈовић