我在一些C++代码中有一个调度表.它将标记映射到可以处理这些标记的函数.在第一个版本中,它接受接受两个字符串并返回字符串的函数.字符串是序列化的protobufs.
map<string, function<string(const string& serialised_1,
const string& serialised_2)>> converters = {
...
{ 'dog', ProcessTwoDogs },
{ 'cat', ProcessTwoCats },
...
};
Run Code Online (Sandbox Code Playgroud)
转换器功能在这里看起来像这样
string ProcessTwoDogs(const string& dog_1_str, const string& dog_2_str);
Run Code Online (Sandbox Code Playgroud)
在实现了相当多的这些转换器后,我意识到它们经常超过一半样板:错误检查,反序列化,序列化等等.所以我写了一个快速模板,大大简化了我的代码:
template <typename ProtoT>
std::string ConvertProtos(
const std::string& proto_str_a,
const std::string& proto_str_b,
std::function<ProtoT(const ProtoT&, const ProtoT&)> convert_proto) {
ProtoT proto_a = ...;
ProtoT probo_b = ...;
// and various error checks.
ProtoT proto_out = convert_proto(proto_a, proto_b);
// some more checks, and serialise to proto_out_str.
return proto_out_str;
}
Run Code Online (Sandbox Code Playgroud)
现在convert_proto()看起来像这样:
Dog ProcessTwoDogs(const Dog& dog_1, const Dog& dog_2) { ... }
Run Code Online (Sandbox Code Playgroud)
这是非常好的,但现在我已经打破了调度表,因为每个动物处理器都有不同的签名,因为a Dog和a Cat都是protobufs,但在其他方面是不相关的.我不知道如何制作一个调度表而不需要花费很多时间if ... else if .....
我想要的是这样的地图:
// Doesn't compile.
map<string,
template<typename ProtoT>function<ProtoT(const ProtoT&, const ProtoT&)>>
Run Code Online (Sandbox Code Playgroud)
然后我的函数使用调度表,目前说的类似于
auto processor = the_map.at(tag);
string new_string = processor(string_1, string_2);
Run Code Online (Sandbox Code Playgroud)
变
auto processor = the_map.at(tag);
string new_string = ConvertProtobufs(string_1, string_2, processor);
Run Code Online (Sandbox Code Playgroud)
当然,一种方法是定义一个带有operator()字符串的抽象基类,然后为每个转换函数实现该类的实例.在operator()调用一些功能只在派生类中定义.但是现在我已经失去了可能的可读性或简洁性.
有什么建议?
按照@felix提出的一系列推理,我写道:
#include <functional>
#include <iostream>
#include <map>
#include <string>
using std::cout;
using std::function;
using std::endl;
using std::map;
using std::string;
struct Dog {
void operator()() { cout << "I am a dog." << endl; }
};
struct Cat {
void operator()() { cout << "I am a cat." << endl; }
};
string cat = string("cat");
string dog = string("dog");
template<string& s> void fn() { cout << "I am lost" << endl; }
template<> void fn<dog>() { Dog dog; dog(); }
template<> void fn<cat>() { Cat cat; cat(); }
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
fn<dog>();
fn<cat>();
// Oops, it all falls apart here:
string dog1("dog");
fn<dog1>(); // Doesn't compile, and a dog is not a dog1.
}
Run Code Online (Sandbox Code Playgroud)
上面的问题是模板参数当然必须在编译时知道.当我使用来自字符串本体的const字符串时,这很好,但是如果字符串通过数据库就会失败,因此查找是基于值而不是对象的动态.
你仍然想要map<string, function<string(const string&, const string&)>>,你只是想以不同的方式填充它:
using converter = function<string(const string&, const string&)>;
map<string, converter> converters = {
...
{ "dog", convert_protos(ProcessTwoDogs) },
{ "cat", convert_protos(ProcessTwoCats) },
...
};
Run Code Online (Sandbox Code Playgroud)
现在您需要一个返回转换器的函数模板,您可以将其与任何ProcessTwoDogs、ProcessTwoCats以及您拥有的任何内容一起使用。
template <typename P_Res, typename P_A, typename P_B>
converter convert_protos(P_Res (*processor)(P_A, P_B)) {
return [](const string& s_a, const string& s_b) -> string
{
// some error checks
P_A p_a = deserialize<P_A>(s_a);
P_B p_b = deserialize<P_B>(s_a);
// some more error checks
P_Res p_res = processor(p_a, p_b);
// yet more checks
string s_res = serialize(p_res);
// last final checks
return s_res;
};
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1069 次 |
| 最近记录: |