Search code examples
c++unique-ptrfactory-pattern

Factory pattern with unique_ptr


I have abstract class Agent, and few derived: Predator, Prey etc. I would like to make a factory, that gives me unique_ptr which I can store in vector of base class.

Problem is, when I have:

using creatorFunctionType = std::unique_ptr<Agent>(*)();

this part is ok, but in map I cannot use lambda with:

return std::make_unique<Predator>();

But when I am trying to use template:

template <class T>
using creatorFunctionType = std::unique_ptr<T>(*)();

The rest of functions doesn't compile. I am nearby sure, I missed something important about templates, but no idea what. Can you give me some hints?

Posting full code, may be helpful

AgentFactory.h

#include "Interfaces/Agent.h"
#include "Enums.h"
#include <map>
#include <memory>

class AgentFactory
{
public:
    template <class T>
    using creatorFunctionType = std::unique_ptr<T>(*)();

    AgentFactory();
    std::unique_ptr<Agent> createAgent(Enums::AgentType agentType);
private:
    void registerAgentType(Enums::AgentType agentType, creatorFunctionType 
    creatorFunction);

    std::map<Enums::AgentType, creatorFunctionType> factoryRegister;
};

AgentFactory.cpp

#include "AgentFactory.h"
#include "Predator.h"
#include "Prey.h"

AgentFactory::AgentFactory()
{
    registerAgentType(Enums::AgentType::Predator, []() { return         
    std::make_unique<Predator>(); });
    registerAgentType(Enums::AgentType::Prey, []() { return     
    std::make_unique<Prey>(); });
}

std::unique_ptr<Agent> AgentFactory::createAgent(Enums::AgentType 
agentType)
{
    if (auto it = factoryRegister.find(agentType); it != 
    factoryRegister.end()) {
        return it->second();
    }

    return nullptr;
}

void AgentFactory::registerAgentType(Enums::AgentType agentType, 
creatorFunctionType creatorFunction)
{
    factoryRegister.insert(std::pair<Enums::AgentType, 
    creatorFunctionType>(agentType, creatorFunction));
}

Compilation errors:

1>d:\predator-prey\predator-prey\agentfactory.h(15): error C2955: 'AgentFactory::creatorFunctionType': use of alias template requires template argument list
1>d:\predator-prey\predator-prey\agentfactory.h(10): note: see declaration of 'AgentFactory::creatorFunctionType'
1>d:\predator-prey\predator-prey\agentfactory.h(17): error C3203: 'creatorFunctionType': unspecialized alias template can't be used as a template argument for template parameter '_Ty', expected a real type
1>d:\predator-prey\predator-prey\agentfactory.cpp(14): error C2064: term does not evaluate to a function taking 0 arguments
1>d:\predator-prey\predator-prey\agentfactory.cpp(22): error C3203: 'creatorFunctionType': unspecialized alias template can't be used as a template argument for template parameter '_Ty2', expected a real type
1>d:\predator-prey\predator-prey\agentfactory.cpp(22): fatal error C1903: unable to recover from previous error(s); stopping compilation

Solution

  • When creatorFunctionType is defined as

    using creatorFunctionType = std::unique_ptr<Agent>(*)();
    

    creatorFunctionType is a pointer-to-function that expects functions which return a std::unique_ptr<Agent>.

    However, your lambdas do not have explicit return types, so the compiler deduces their return types as std::unique_ptr<Predator> and std::unique_ptr<Prey>, respectively, based on their return statements.

    A non-capturing lambda is implicitly convertible to a pointer-to-function, which is what you want in this case, however your lambdas do not return std::unique_ptr<Agent>, so they cannot be assigned to creatorFunctionType. The types simply do not match.

    You need to be explicit about the return type of your lambdas so they match the correct signature that creatorFunctionType is expecting, eg:

    AgentFactory::AgentFactory()
    {
        registerAgentType(Enums::AgentType::Predator,
            []() -> std::unique_ptr<Agent> { return std::make_unique<Predator>(); }
        );
        registerAgentType(Enums::AgentType::Prey,
            []() -> std::unique_ptr<Agent> { return std::make_unique<Prey>(); }
        );
    }
    

    With the above code, the lambdas will now return std::unique_ptr<Agent>, satisfying what creatorFunctionType expects. And the return statements still work as-is because std::unique_ptr<T> can be initialized with a std::unique_ptr<U> as long as U derives from T, which is true in your case since Predator and Prey derive from Agent.