gpt4 book ai didi

c++ - 在不使用外部库的情况下在运行时动态地从映射转换和调用可变参数函数的好方法是什么

转载 作者:行者123 更新时间:2023-12-03 06:50:14 25 4
gpt4 key购买 nike

假设我有多个带有可变参数的函数:

void greetWorld() {
cout << "Hello World!" << endl;
}

void greetName(const string& name) {
cout << "Hello " << name << "!" << endl;
}

void printAddition(const int lhs, const int rhs) {
cout << "Addition: " << to_string(lhs + rhs) << endl;
}
这些都存储在 std::strings 的映射中到函数(函数被存储为多态类)。
template<typename... Args>
class DerivedFunction;

class BaseFunction {
public:
template<typename... Args>
void operator()(Args... args) const {
(*static_cast<const DerivedFunction<Args...>*>(this))(args...);
}
};

template<typename... Args>
class DerivedFunction : public BaseFunction {
public:
DerivedFunction(void(*function)(Args...)) {
this->function = function;
}
void operator()(Args... args) const {
function(args...);
}
private:
void(*function)(Args...);
};

template<typename... Args>
unique_ptr<DerivedFunction<Args...>> make_function(
void(*function)(Args...)
) {
return std::make_unique<DerivedFunction<Args...>>(function);
}

int main() {
unordered_map<string, unique_ptr<BaseFunction>> function_map;
function_map.insert({ "greetWorld", make_function(&greetWorld) });
function_map.insert({ "greetName", make_function(&greetName) });
function_map.insert({ "printAddition", make_function(&printAddition) });

...
}
我可以在编译时调用函数,如:
int main() {
...

(*function_map.at("greetWorld"))();
(*function_map.at("greetName"))("Foo"s);
(*function_map.at("printAddition"))(1, 2);
}
如果我有一个字符串,或者像这样的流:
greetWorld
greetName string Foo
printAddition int 1 int 2
调用函数的好方法是什么?
我想不出任何在运行时转换类型的方法。

为什么?
我正在尝试实现某种远程调用过程以用于学习目的。我不想使用外部库,因为我正在尝试学习如何使用 C++ 标准库来实现它,以便更多地了解 C++。

我尝试了什么?
不多。我已经测试了创建采用 std::vector 的函数的 std::any s 作为参数,然后有了函数 any_cast他们的类型。虽然这确实有效,但它看起来并不好,它需要所有函数的拷贝,我宁愿能够编写具有有意义参数的函数,而不是模棱两可的。

最小示例
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
using namespace std;

void greetWorld() {
cout << "Hello World!" << endl;
}

void greetName(const string& name) {
cout << "Hello " << name << "!" << endl;
}

void printAddition(const int lhs, const int rhs) {
cout << "Addition: " << to_string(lhs + rhs) << endl;
}

template<typename... Args>
class DerivedFunction;

class BaseFunction {
public:
template<typename... Args>
void operator()(Args... args) const {
(*static_cast<const DerivedFunction<Args...>*>(this))(args...);
}
};

template<typename... Args>
class DerivedFunction : public BaseFunction {
public:
DerivedFunction(void(*function)(Args...)) {
this->function = function;
}
void operator()(Args... args) const {
function(args...);
}
private:
void(*function)(Args...);
};

template<typename... Args>
unique_ptr<DerivedFunction<Args...>> make_function(
void(*function)(Args...)
) {
return std::make_unique<DerivedFunction<Args...>>(function);
}

int main() {
unordered_map<string, unique_ptr<BaseFunction>> function_map;
function_map.insert({ "greetWorld", make_function(&greetWorld) });
function_map.insert({ "greetName", make_function(&greetName) });
function_map.insert({ "printAddition", make_function(&printAddition) });

cout << "Calling functions at compile time." << endl << endl;

(*function_map.at("greetWorld"))();
(*function_map.at("greetName"))("Foo"s);
(*function_map.at("printAddition"))(1, 2);

//cout << endl << "Calling functions at runtime." << endl << endl;
//string runtime =
// "greetWorld\n"
// "greetName string Foo\n"
// "printAddition int 1 int 2";
//
// todo: call functions
}

解决了。
如果您应用接受的解决方案,您可以像我想要的那样从文本中调用函数。
这是示例 Tcp 服务器和客户端的新代码。客户端将函数名称和参数作为字符串发送到服务器。然后服务器执行这些。正是我想要的。
struct FunctionNameAndArguments {
string function_name;
vector<RPC> arguments;
};

FunctionNameAndArguments parseFunctionNameAndArguments(
const string& function_name_and_arguments_string
) {
istringstream ss(function_name_and_arguments_string);
FunctionNameAndArguments function_name_and_arguments;
// function name
ss >> function_name_and_arguments.function_name;
// arguments
auto& arguments = function_name_and_arguments.arguments;
while (!ss.eof()) {
string function_type;
ss >> function_type;
// integer
if (function_type == "int") {
int value;
ss >> value;
arguments.push_back(value);
}
// string
else if (function_type == "string") {
string value;
ss >> value;
arguments.push_back(value);
}
else {
throw exception("unknown argument type");
}
}
return function_name_and_arguments;
}

int main() {
unordered_map<string, RPCHandler> functions = {
{ "greetWorld", make_invoker(&greetWorld) },
{ "greetName", make_invoker(&greetName) },
{ "printAddition", make_invoker(&printAddition) }
};

char server;
cout << "Server? (y/n): " << endl;
cin >> server;
// server
if (server == 'y') {
// accept client
TcpListener listen;
listen.listen(25565);
TcpSocket client;
listen.accept(client);

size_t received;
// receive size of string
size_t size;
client.receive(&size, sizeof(size), received);
// receive function name and arguments as string
string function_name_and_arguments_string;
function_name_and_arguments_string.resize(size);
client.receive(
function_name_and_arguments_string.data(),
size,
received
);
// go through each line
istringstream lines(function_name_and_arguments_string);
string line;
while (getline(lines, line)) {
// parse function name and arguments
auto [function_name, arguments] = parseFunctionNameAndArguments(
line
);
// call function
functions.at(function_name)(
arguments
);
}

}
// client
else {
// connect to server
TcpSocket server;
server.connect("localhost", 25565);

// function calls string
const string function_calls =
"greetWorld\n"
"greetName string Foo\n"
"printAddition int 1 int 2";
size_t size = function_calls.size();
// send size of string
server.send(&size, sizeof(size));
// send function calls string
server.send(function_calls.data(), size);
}
}

最佳答案

假设您有一个可用于 RPC 的类型列表(以 int 和 string 为例),我们可以将它们组合在 RPC 中。类型和关联 RPCHandler如下:

using RPC = std::variant<int, std::string>;
using RPCHandler = std::function<void(std::vector<RPC>)>;
您要创建一个 std::map<std::string, RPCHandler> dispatch所以你可以这样做(给定 std::vector<RPC> args ):
dispatch[command](args);
该 map 可以构造如下:
void test0();
void test2(int, std::string);

std::map<std::string, RPCHandler> dispatch = {
{ "test0", make_invoker(test0) },
{ "test2", make_invoker(test2) },
};
哪里 make_invoker返回正确形状的 lambda。
此 lambda 的主体传递函数指针、参数 vector 和 std::index_sequenceinvoke_rpc :
template<class... Arg>
RPCHandler make_invoker(void (*f)(Arg...)) {
return [f](std::vector<RPC> args) {
invoke_rpc(f, args, std::index_sequence_for <Arg...>{});
};
}
最后, invoke_rpc用途 std::get在每个参数上依次将其转换为预期的类型。它通过并行扩展两个给定的模板参数包来实现这一点。直观地,这扩展为 f(std::get<Arg0>(args.at(0), std::get<Arg1>(args.at(1))f 一样多的参数正如预期的那样(因为索引序列具有相同的长度 Args... )。
template<class... Arg, std::size_t... I>
void invoke_rpc(void (*f)(Arg...), std::vector<RPC> args, std::index_sequence<I...>) {
f(std::get<Arg>(args.at(I))...);
}
如果 vector 太短,你会得到一个 std::out_of_range错误,如果参数不匹配,你会得到 std::bad_variant_access .您可以通过检查 args 的大小来改进错误处理。调用前 f ,并使用 std::holds_alternative查看所有传递的值是否与其被禁止的类型匹配。

关于c++ - 在不使用外部库的情况下在运行时动态地从映射转换和调用可变参数函数的好方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64695613/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com