2
\$\begingroup\$

I've developed as part of a game engine a template class for a factory which manages objects of one specific type. The factory handles initialization, destruction, and accessing previously created objects by string identifier.

It allows forwarding parameters to object constructors with variadic arguments, and is interfaced with a few freestanding functions within the namespace. Since each object type has its own factory, factories are created automatically and managed by the Game class (in the actual code Game is a singleton and handles a bunch of stuff but in the following simplified code, Game just deletes all the automatically created factories for us).

The following code can be copied to a file and compiled, it runs as expected and includes a simple example. Compile with C++11 enabled.

#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <memory>

namespace TGE
{
    class FactoryBase
    {
        public:
            virtual void destroy() = 0;
    };

    class Game
    {
        public:
            void manageFactory(FactoryBase* factory)
            {
                _factoryStack.push_back(factory);
            }

            void destroy()
            {
                while(!_factoryStack.empty())
                {
                    _factoryStack.back()->destroy();
                    _factoryStack.pop_back();
                }
            }

        private:
            std::vector<FactoryBase*> _factoryStack;
    } MyGame;

    // Forward declare template function for friendship later in ObjectFactory
    template<class ObjectClass>
    std::vector<std::string> objectList();

    template<class ObjectClass>
    class ObjectFactory : public FactoryBase
    {
        public:
            void destroy()
            {
                if(_instance != nullptr)
                    delete _instance;
            }

            static ObjectFactory<ObjectClass>* instance()
            {
                if(_instance == nullptr)
                    _instance = new ObjectFactory<ObjectClass>();

                return _instance;
            }

            template<typename... Args>
            std::shared_ptr<ObjectClass> createObject(std::string id, Args&&... args)
            {
                objectMap[id] = std::shared_ptr<ObjectClass>
                                               (new ObjectClass(std::forward<Args>(args)...));
                return objectMap[id];
            }

            std::shared_ptr<ObjectClass> getObject(std::string id)
            {
                if(objectMap.find(id) != objectMap.end())
                    return objectMap[id];

                return nullptr;
            }

            void deleteObject(std::string id)
            {
                if(objectMap.find(id) != objectMap.end())
                    objectMap.erase(id);
            }

        private:
            ObjectFactory<ObjectClass>() { MyGame.manageFactory(this); }
            ~ObjectFactory<ObjectClass>() {}

            static ObjectFactory<ObjectClass>* _instance;

            friend std::vector<std::string> objectList<ObjectClass>();

            std::map<std::string, std::shared_ptr<ObjectClass>> objectMap;
    };

    template<class ObjectClass>
    ObjectFactory<ObjectClass>* ObjectFactory<ObjectClass>::_instance = nullptr;

    template<class ObjectClass, typename... Args>
    std::shared_ptr<ObjectClass> construct(std::string id, Args&&... args)
    {
        return ObjectFactory<ObjectClass>::instance()->createObject(id, std::forward<Args>(args)...);
    }

    template<class ObjectClass>
    std::shared_ptr<ObjectClass> acquire(std::string id)
    {
        return ObjectFactory<ObjectClass>::instance()->getObject(id);
    }

    template<class ObjectClass>
    void destruct(std::string id)
    {
        ObjectFactory<ObjectClass>::instance()->deleteObject(id);
    }

    template<class ObjectClass>
    std::vector<std::string> objectList()
    {
        std::vector<std::string> keys;

        for(auto itr : ObjectFactory<ObjectClass>::instance()->objectMap)
        {
            keys.push_back(itr.first);
        }

        return keys;
    }
}

class Rectangle
{
    public:
        Rectangle(int w, int h) : width(w), height(h) {}

        void setVals(int w, int h)
        {
            width = w;
            height = h;
        }

        void getVals()
        {
            std::cout << width << 'x' << height << std::endl;
        }

    private:
        int width, height;
};

int main()
{
    auto myRectangle = TGE::construct<Rectangle>("MyRectangle", 5, 10);
    myRectangle->getVals();
    myRectangle->setVals(10, 5);
    TGE::acquire<Rectangle>("MyRectangle")->getVals();

    TGE::MyGame.destroy();
    return 0;
}
\$\endgroup\$
  • \$\begingroup\$ Out of curiosity: Why the destroy function? \$\endgroup\$ – MikeMB Apr 9 '15 at 22:10
  • \$\begingroup\$ The actual code is a singleton Game class which has a static destroy function to close the game, you call it just before returning 0 in your main function to delete the game states, resource manager, object factories etc. The destroy function in ObjectFactory is to delete the static instance of ObjectFactory, as the instance is private. \$\endgroup\$ – Mike Apr 11 '15 at 17:39
2
\$\begingroup\$

Nicely done program.

Must do

  1. Add a virtual destructor to FactoryBase.

Suggestions

  1. objectList() can easily be implemented without the friend construct if ObjectFactory<ObjectClass> has a function, such as ObjectFactory<ObjectClass>::getObjectList(), that returns the right object.

    template<class ObjectClass>
    std::vector<std::string> objectList()
    {
        return ObjectFactory<ObjectClass>::instance()->getObjectList();
    }
    
  2. You have most functions named consistently -- with a verb as the first term -- except objectList(). That should be changed to getObjectList().

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.