C++ 多态工厂如何从字符串名称和参数映射构建函数对象
C++ polymorphism factory How to build function object from its string name and map of parameter
#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";
}
我正在尝试创建一个类似于 Python 的 C++ 库,用户将在其中提供 map
字符串和参数。代码会将映射解析为函数,然后执行函数。最重要的是,我需要能够写:
std::any param_map; // = something, doesn't matter here
FunctionBase *myfunc = new functionMapping["myfunc"](param_map);
给定示例中的代码,
的值类型应该是什么
map<string, ???> functionMapping;
编辑: 添加了一个使用丑陋的 if 语句的等效解决方案
答案是使用 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);
您可以使用基于模板的抽象工厂。这是非常通用的,可用于不同类型的键和值。
也许下面的通用代码可以让您了解如何实现您的工厂。
#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;
}
#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";
}
我正在尝试创建一个类似于 Python 的 C++ 库,用户将在其中提供 map
字符串和参数。代码会将映射解析为函数,然后执行函数。最重要的是,我需要能够写:
std::any param_map; // = something, doesn't matter here
FunctionBase *myfunc = new functionMapping["myfunc"](param_map);
给定示例中的代码,
的值类型应该是什么map<string, ???> functionMapping;
编辑: 添加了一个使用丑陋的 if 语句的等效解决方案
答案是使用 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);
您可以使用基于模板的抽象工厂。这是非常通用的,可用于不同类型的键和值。
也许下面的通用代码可以让您了解如何实现您的工厂。
#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;
}