c++ 同一个类中多个构造函数的工厂方法

kqlmhetl  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(176)

我有这个代码,这是一个适应从How to pass arguments to factory elements constructors?工作与智能指针。

#include <unordered_map>
#include <string>
#include <iostream>
#include <memory>
 
class Base;

class myFactory
{
public:
    typedef std::unordered_map<std::string, void*> registry_map;
 
    virtual ~myFactory() = default;
 
    static registry_map & registry()
    {
        static registry_map impl;
        return impl;
    }

    template<typename ...T>
    static std::shared_ptr<Base> instantiate(std::string const & name, T&&...args)
    {
        auto it = registry().find(name);
        if ( it == registry().end()) return 0;
        typedef std::shared_ptr<Base> (*create_type)(T...);
        auto create_fun = reinterpret_cast<create_type>(it->second);
        return create_fun(args...);
    }

    template<typename F>
    static bool sign(const std::string& name, F func)
    {
        registry()[name] = reinterpret_cast<void*>(func);
        return true;
    }
};
 
class Base: public myFactory
{
    public:
        virtual void f() = 0;
        virtual ~Base() = default;
};
 
class DerivedExample : public Base
{
private:
    static bool sign;

public:
    DerivedExample(int a, int b){std::cout << a << b << std::endl;}
    DerivedExample() = default;
    
    static std::shared_ptr<Base> create() { return std::make_shared<DerivedExample>();}
    static std::shared_ptr<Base> create(int a, int b) { return std::make_shared<DerivedExample>(a,b);}

    virtual void f() override { std::cout << "DerivedExample" << std::endl; }
};
 
bool DerivedExample::sign = DerivedExample::myFactory::sign("DerivedExample", DerivedExample::create());
bool DerivedExample::sign = DerivedExample::myFactory::sign("DerivedExample", DerivedExample::create(int a, int b)); // redefinition

int main()
{
    std::shared_ptr<Base> p1 = Base::instantiate("DerivedExample");
    std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample", 1, 2);
    p1->f();
    p2->f();

}

由于sign boolean的重定义,这个实现不允许我为工厂注册2个构造函数。有没有办法修复代码,以便为同一个类注册多个create函数?
最好的问候

6kkfgxo0

6kkfgxo01#

bool sign用于全局范围内的“自动”注册。您可以添加额外的变量:

class DerivedExample : public Base
{
private:
    static bool sign1;
    static bool sign2;
    // ...
};

bool DerivedExample::sign1 = DerivedExample::myFactory::sign("DerivedExample1", static_cast<std::shared_ptr<Base> (*)()>(&DerivedExample::create));
bool DerivedExample::sign2 = DerivedExample::myFactory::sign("DerivedExample2", static_cast<std::shared_ptr<Base> (*)(int, int)>(&DerivedExample::create));

顺便说一句,注册的方式是错误的,特别是当涉及过载时。
Demo

y3bcpkx1

y3bcpkx12#

一种修复代码的方法:

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

class Base;

class myFactory {
   public:
    typedef std::unordered_map<std::string, void*> registry_map;

    virtual ~myFactory() = default;

    static registry_map& registry() {
        static registry_map impl;
        return impl;
    }

    // here needing to pass by value in order to match your create function
    // signature
    template <typename... T>
    static std::shared_ptr<Base> instantiate(std::string const& name,
                                             T... args) {
        auto it = registry().find(name);
        if (it == registry().end()) return 0;
        typedef std::shared_ptr<Base> (*create_type)(T...);
        auto create_fun = reinterpret_cast<create_type>(it->second);
        return create_fun(args...);
    }

    template <typename F>
    static bool sign(const std::string& name, F func) {
        registry()[name] = reinterpret_cast<void*>(func);
        return true;
    }
};

class Base : public myFactory {
   public:
    virtual void f() = 0;
    virtual ~Base() = default;
};

class DerivedExample : public Base {
   public:
    DerivedExample(int a, int b) { std::cout << a << " " << b << std::endl; }
    DerivedExample() { std::cout << "default" << std::endl; };

    static std::shared_ptr<Base> create() {
        return std::make_shared<DerivedExample>();
    }
    static std::shared_ptr<Base> create(int a, int b) {
        return std::make_shared<DerivedExample>(a, b);
    }

    virtual void f() override { std::cout << "DerivedExample" << std::endl; }

   private:
    // using one bool per function to register
    static bool sign1;
    static bool sign2;
    // properly getting the overloaded function addresses
    static std::shared_ptr<Base> (*pcreate1)();
    static std::shared_ptr<Base> (*pcreate2)(int, int);
};

std::shared_ptr<Base> (*DerivedExample::pcreate1)() = &DerivedExample::create;
std::shared_ptr<Base> (*DerivedExample::pcreate2)(int, int) =
    &DerivedExample::create;
// correctly passing the create function addresses
bool DerivedExample::sign1 =
    DerivedExample::sign("DerivedExample", DerivedExample::pcreate1);
bool DerivedExample::sign2 =
    DerivedExample::sign("DerivedExample2", DerivedExample::pcreate2);

int main() {
    std::shared_ptr<Base> p1 = Base::instantiate("DerivedExample");
    std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample2", 1, 2);
    p1->f();
    p2->f();
}

Live
但我不确定这是最好的设计。我更倾向于只为继承类注册一个(尽管上面的提议表明可以不这样做)。此外,变量签名是错误的,我通过将转发引用替换为按值传递来修复它,但这不一定是你想要做的。
[编辑]这是一个没有variadic的解决方案,只需要一个注册,遵循我在评论中的建议。

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

class Base;

class myFactory {
   public:
    typedef std::unordered_map<std::string, void *> registry_map;

    virtual ~myFactory() = default;

    static registry_map &registry() {
        static registry_map impl;
        return impl;
    }

    // here needing to pass by value in order to match your create function
    // signature
    template <typename... T>
    static std::shared_ptr<Base> instantiate(std::string const &name,
                                             T... args) {
        auto it = registry().find(name);
        if (it == registry().end()) return 0;
        typedef std::shared_ptr<Base> (*create_type)(T...);
        auto create_fun = reinterpret_cast<create_type>(it->second);
        return create_fun(args...);
    }

    template <typename F>
    static bool reg(const std::string &name, F func) {
        registry()[name] = reinterpret_cast<void *>(func);
        return true;
    }
};

class Base : public myFactory {
   public:
    virtual void f() = 0;
    virtual ~Base() = default;
};

// abstract base parameter class
struct Parameter {
    virtual ~Parameter() = 0;
};
Parameter::~Parameter() = default;

// parameter for constructor from void
struct VoidParameter : public Parameter {};

// parameter for constructor from two ints
struct TwoIntParameter : public Parameter {
    int a;
    int b;
    TwoIntParameter(int first, int second) : a(first), b(second) {}
};

class DerivedExample : public Base {
   public:
    DerivedExample(int a, int b) { std::cout << a << " " << b << std::endl; }
    DerivedExample() { std::cout << "default" << std::endl; };

    static std::shared_ptr<Base> create(const Parameter *param) {
        if (dynamic_cast<const VoidParameter *>(param)) {
            return std::make_shared<DerivedExample>();
        }
        if (dynamic_cast<const TwoIntParameter *>(param)) {
            TwoIntParameter const *ptr =
                dynamic_cast<const TwoIntParameter *>(param);
            return std::make_shared<DerivedExample>(ptr->a, ptr->b);
        }
        return nullptr;
    }

    virtual void f() override { std::cout << "DerivedExample" << std::endl; }

   private:
    // using one bool per function to register
    static bool sign;
};

// correctly passing the create function addresses
bool DerivedExample::sign =
    DerivedExample::reg("DerivedExample", &DerivedExample::create);

int main() {
    VoidParameter v;
    TwoIntParameter ti{1, 2};
    std::shared_ptr<Base> p1 = Base::instantiate("DerivedExample", &v);
    std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample", &ti);
    p1->f();
    p2->f();
}

Live
你必须为每个你想要支持的构造函数创建一个派生的参数类型。你可以根据你真正想要达到的目标来避免重复。

相关问题