C++多态工厂如何从字符串名称和参数映射构建函数对象

发布于 2025-01-17 07:35:05 字数 2482 浏览 1 评论 0原文

#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <any>
#include <functional>
#include <type_traits>
using namespace std;

using MapAny = map<string, any>;

class FunctionBase {
public:
    FunctionBase() {}
    FunctionBase(const MapAny &param_maps) {}
    virtual any operator()(const any &data) const = 0;
};

class Func1 : public FunctionBase {
private:
    int a,b;

public:
    Func1(const MapAny &param_map) {
        a = any_cast<int>(param_map.at("a"));
        b = any_cast<int>(param_map.at("b"));
    }

    virtual any operator()(const any &data) const override {        
        // calculate some F1(data, a, b)
        return a + b;
    }
};

class Func2 : public FunctionBase {
private:
    float c,d;    
public:
    Func2(const MapAny &param_map) {        
        c = any_cast<float>(param_map.at("c"));
        d = any_cast<float>(param_map.at("d"));
    }

    virtual any operator()(const any &data) const override {
        // calculate some F2(data, a, b)
        return c * d;
    }
};

FunctionBase* functionFactory(string name, const MapAny &param_map)
{
    if (name=="func1") return new Func1(param_map);
    if (name=="func2") return new Func2(param_map);
    return nullptr;
}

int main()
{
    // map<string, ???> functionMapping;
    // functionMapping["func1"] = ...
    // functionMapping["func2"] = ...   
    vector<float> data;

    MapAny param_map1;
    param_map1["a"] = 3;
    param_map1["b"] = 5;    
    FunctionBase* func1 = functionFactory("func1", param_map1); //functionMapping["func1"](param_map1);
    assert(any_cast<int>(func1->operator()(data))==8);

    MapAny param_map2;
    param_map2["c"] = 2.0f;
    param_map2["d"] = 4.0f;
    FunctionBase* func2 = functionFactory("func2", param_map2); //functionMapping["func2"](param_map2);
    assert(any_cast<float>(func2->operator()(data))==8.0);

    cout << "Correct\n";
}

我正在尝试创建一个类似 Python 的 C++ 库,用户将在其中提供字符串和参数的 map 。代码会将映射解析为函数,然后执行函数。最重要的是,我需要能够编写:

std::any param_map; // = something, doesn't matter here
FunctionBase *myfunc = new functionMapping["myfunc"](param_map);

给定示例中的代码,

map<string, ???> functionMapping;

Edit: 的值类型应该是什么:使用丑陋的 if 语句添加了等效的解决方案

#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <any>
#include <functional>
#include <type_traits>
using namespace std;

using MapAny = map<string, any>;

class FunctionBase {
public:
    FunctionBase() {}
    FunctionBase(const MapAny ¶m_maps) {}
    virtual any operator()(const any &data) const = 0;
};

class Func1 : public FunctionBase {
private:
    int a,b;

public:
    Func1(const MapAny ¶m_map) {
        a = any_cast<int>(param_map.at("a"));
        b = any_cast<int>(param_map.at("b"));
    }

    virtual any operator()(const any &data) const override {        
        // calculate some F1(data, a, b)
        return a + b;
    }
};

class Func2 : public FunctionBase {
private:
    float c,d;    
public:
    Func2(const MapAny ¶m_map) {        
        c = any_cast<float>(param_map.at("c"));
        d = any_cast<float>(param_map.at("d"));
    }

    virtual any operator()(const any &data) const override {
        // calculate some F2(data, a, b)
        return c * d;
    }
};

FunctionBase* functionFactory(string name, const MapAny ¶m_map)
{
    if (name=="func1") return new Func1(param_map);
    if (name=="func2") return new Func2(param_map);
    return nullptr;
}

int main()
{
    // map<string, ???> functionMapping;
    // functionMapping["func1"] = ...
    // functionMapping["func2"] = ...   
    vector<float> data;

    MapAny param_map1;
    param_map1["a"] = 3;
    param_map1["b"] = 5;    
    FunctionBase* func1 = functionFactory("func1", param_map1); //functionMapping["func1"](param_map1);
    assert(any_cast<int>(func1->operator()(data))==8);

    MapAny param_map2;
    param_map2["c"] = 2.0f;
    param_map2["d"] = 4.0f;
    FunctionBase* func2 = functionFactory("func2", param_map2); //functionMapping["func2"](param_map2);
    assert(any_cast<float>(func2->operator()(data))==8.0);

    cout << "Correct\n";
}

I'm trying to make a Python-like C++ library, where user will give a map of string and parameters. The code will parse the map to functions, then execute the functions. Most importantly, I need to be able to write:

std::any param_map; // = something, doesn't matter here
FunctionBase *myfunc = new functionMapping["myfunc"](param_map);

Given the code in the example, what should be the value type of

map<string, ???> functionMapping;

Edit: added an equivalent solution using ugly if-statement

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

顾冷 2025-01-24 07:35:05

您可以使用基于模板的抽象工厂。这是非常通用的,可以用于不同类型的键和值。

也许下面的通用代码可以让您了解如何实现您的工厂。

#include <iostream>
#include <map>
#include <utility>
#include <any>


// Some demo classes ----------------------------------------------------------------------------------
struct Base {
    Base(int d) : data(d) {};
    virtual ~Base() { std::cout << "Destructor Base\n"; }
    virtual void print() { std::cout << "Print Base\n"; }
    int data{};
};
struct Child1 : public Base {
    Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
    virtual ~Child1() { std::cout << "Destructor Child1\n"; }
    virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
};
struct Child2 : public Base {
    Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
    virtual ~Child2() { std::cout << "Destructor Child2\n"; }
    virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
};
struct Child3 : public Base {
    Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
    virtual ~Child3() { std::cout << "Destructor Child3\n"; }
    virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
};



using UPTRB = std::unique_ptr<Base>;


template <class Child, typename ...Args>
UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }

// The Factory ----------------------------------------------------------------------------------------
template <class Key, class Object>
class Factory
{
    std::map<Key, std::any> selector;
public:
    Factory() : selector() {}
    Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}

    template<typename Function>
    void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };

    template <typename ... Args>
    Object create(Key key, Args ... args) {
        if (selector.find(key) != selector.end()) {
            return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
        }
        else return nullptr;
    }
};

int main()
{
    // Define the factory with an initializer list
    Factory<int, UPTRB> factory{
        {1, createClass<Child1, int, std::string>},
        {2, createClass<Child2, int, char, long>}
    };

    // Add a new entry for the factory
    factory.add(3, createClass<Child3, int, long, char, std::string>);


    // Some test values
    std::string s1(" Hello1 "); std::string s3(" Hello3 ");
    int i = 1;  const int ci = 1;   int& ri = i;    const int& cri = i;   int&& rri = 1;

    UPTRB b1 = factory.create(1, 1, s1);
    UPTRB b2 = factory.create(2, 2, '2', 2L);
    UPTRB b3 = factory.create(3, 3, 3L, '3', s3);

    b1->print();
    b2->print();
    b3->print();
    b1 = factory.create(2, 4, '4', 4L);
    b1->print();
    return 0;
}

You may use an template based abstract factory. This is very generic and can be used for different types of keys and values.

Maybe the following generic code gives you an idea, how you could implement your factory.

#include <iostream>
#include <map>
#include <utility>
#include <any>


// Some demo classes ----------------------------------------------------------------------------------
struct Base {
    Base(int d) : data(d) {};
    virtual ~Base() { std::cout << "Destructor Base\n"; }
    virtual void print() { std::cout << "Print Base\n"; }
    int data{};
};
struct Child1 : public Base {
    Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
    virtual ~Child1() { std::cout << "Destructor Child1\n"; }
    virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
};
struct Child2 : public Base {
    Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
    virtual ~Child2() { std::cout << "Destructor Child2\n"; }
    virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
};
struct Child3 : public Base {
    Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
    virtual ~Child3() { std::cout << "Destructor Child3\n"; }
    virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
};



using UPTRB = std::unique_ptr<Base>;


template <class Child, typename ...Args>
UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }

// The Factory ----------------------------------------------------------------------------------------
template <class Key, class Object>
class Factory
{
    std::map<Key, std::any> selector;
public:
    Factory() : selector() {}
    Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}

    template<typename Function>
    void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };

    template <typename ... Args>
    Object create(Key key, Args ... args) {
        if (selector.find(key) != selector.end()) {
            return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
        }
        else return nullptr;
    }
};

int main()
{
    // Define the factory with an initializer list
    Factory<int, UPTRB> factory{
        {1, createClass<Child1, int, std::string>},
        {2, createClass<Child2, int, char, long>}
    };

    // Add a new entry for the factory
    factory.add(3, createClass<Child3, int, long, char, std::string>);


    // Some test values
    std::string s1(" Hello1 "); std::string s3(" Hello3 ");
    int i = 1;  const int ci = 1;   int& ri = i;    const int& cri = i;   int&& rri = 1;

    UPTRB b1 = factory.create(1, 1, s1);
    UPTRB b2 = factory.create(2, 2, '2', 2L);
    UPTRB b3 = factory.create(3, 3, 3L, '3', s3);

    b1->print();
    b2->print();
    b3->print();
    b1 = factory.create(2, 4, '4', 4L);
    b1->print();
    return 0;
}
彩虹直至黑白 2025-01-24 07:35:05

答案是使用 lambda 和 std::function

map<string, std::function<FunctionBase*(const MapAny&)>> functionMapping;
functionMapping["func1"] = [](const MapAny& prmap) {return new Func1(prmap);};
functionMapping["func2"] = [](const MapAny& prmap) {return new Func2(prmap);};
FunctionBase *func1 = functionMapping["func1"](param_map1);
FunctionBase *func2 = functionMapping["func2"](param_map2);

The answer is using lambda and std::function

map<string, std::function<FunctionBase*(const MapAny&)>> functionMapping;
functionMapping["func1"] = [](const MapAny& prmap) {return new Func1(prmap);};
functionMapping["func2"] = [](const MapAny& prmap) {return new Func2(prmap);};
FunctionBase *func1 = functionMapping["func1"](param_map1);
FunctionBase *func2 = functionMapping["func2"](param_map2);

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文