【发布时间】:2021-01-14 15:39:43
【问题描述】:
我正在开发一个可以与 MPI 一起使用的工具,该工具将使用多个函数,并且每个函数可能彼此完全不同,例如签名和参数数量。 C++ 版本不需要最低版本,我假设这将使用最新版本进行编译。
我的想法是我将推送一些参数和一个函数 ID,并最终将它们序列化以通过 MPI 传递。它将被另一个进程接收,并使用一些反序列化器构建一个元组或参数包,并将其传递给函数#ID。
我想创建一个函数向量,使用这个#ID,它本质上是向量中的索引,我可以选择相应的函数并传递上述元组。
我的工具将接收这个函数向量,然后它们的类型将在编译时知道,元组类型也会在编译时知道,但要使用的函数和元组将取决于收到的#ID。
我已经尝试过这样的 std::variant,但我不知道如何使它工作,因为 C++ 需要在编译时知道数据类型。
#include <iostream>
#include <functional>
#include <variant>
#include <vector>
int foo(int a) {
return a;
}
float bar(int a, float b) {
return (float) a * b;
}
float zor(float a, int b, float c) {
return a * (float) b * c;
}
template<typename F, typename Tuple, size_t... I>
static auto unpack_tuple(F &&f, Tuple &t, std::index_sequence<I...>) {
//I might have to ignore or to add some arguments in here,
//I know the existence of std::apply
return f(std::get<I>(t)...);
}
template<typename F, typename Tuple>
static auto unpack_tuple(F &&f, Tuple &t) {
static constexpr auto size = std::tuple_size<Tuple>::value;
return unpack_tuple(f, t, std::make_index_sequence<size>{});
}
template<typename Functions, typename Tuples>
void magicTool(Functions &functions, Tuples &tuples) {
// receivebuffer using MPI and function id
// use some deserializer and build arguments tuple, using Id to retrieve data types from tuples[id]
//idea
unpack_tuple(functions[id],tuple); //certainly error in this line
}
int main() {
typedef std::function<int(int)> Type1;
typedef std::function<float(int, float)> Type2;
typedef std::function<float(float, int, float)> Type3;
std::vector<std::variant<Type1, Type2, Type3>> functions;
typedef std::tuple<int> Tup1;
typedef std::tuple<int, float> Tup2;
typedef std::tuple<float, int, float> Tup3;
std::vector<std::variant<Tup1, Tup2, Tup3 >> tuples; //this could be also changed by tuple of tuples
functions.emplace_back(foo);
functions.emplace_back(bar);
functions.emplace_back(zor);
// initially just to let the compiler know their type, values won't
// be necessarly used
int a = 3;
float b = 6.4534;
auto t1 = std::make_tuple(a);
auto t2 = std::make_tuple(a, b);
auto t3 = std::make_tuple(b, a, b);
tuples.emplace_back(t1);
tuples.emplace_back(t2);
tuples.emplace_back(t3);
magicTool(functions,tuples);
return 0;
}
考虑到要扩展一个元组,通常是通过创建一个递归助手并使用索引排序来完成的,像这样
template<typename F, typename Tuple, size_t... I>
static auto unpack_tuple(F &&f, Tuple &t, std::index_sequence<I...>) {
return f(std::get<I>(t)...);
}
template<typename F, typename Tuple>
static auto unpack_tuple(F &&f, Tuple &t) {
static constexpr auto size = std::tuple_size<Tuple>::value;
return unpack_tuple(f, t, std::make_index_sequence<size>{});
}
但在这种情况下,我需要同时对变体和元组进行相同的处理,所以最后我有这样的东西
Callable(args...); //Callable must match with parameter pack
我尝试了几个选项,但我总是发现我需要事先知道数据类型并且不能使用条件,因为编译器将能够匹配一个函数并与其他函数相匹配。
我该怎么做?
请在下面找到我想在 C++ 中执行的操作的 python 片段。
import random
def foo(a: int): # function 0
return a
def bar(a: int, b: float): # function 1
return a*b
def zor(a: float, b: int, c: float): # function 2
return a*c+b
def forward(f, *args): # tuple forwarder to any function
return f(*args)
def magicTool(Callables):
packSize: int = random.randrange(3) # to emulate what function I will need
fId = packSize
print("random : {}".format(packSize))
argsPack = [] # to emulate the dynamic size of the incoming arguments
for i in range(packSize+1): # this only matches the parameter with the random function
arg = random.uniform(1.1, 10)
argsPack.append(arg)
print(forward(Callables[fId], *argsPack)) # this tries the function
myCallable = [foo, bar, zor]
magicTool(myCallable)
【问题讨论】:
-
python snip of what I want to do in C++不同的是,python 有反射,c++ 没有。请在您的代码 sn-p 中添加缺少的#includes 和函数foo、bar、zor声明和简短的int main()deifniiton,以便于使用。 -
感谢@alex_noname 的评论,在这种情况下有一个条件,我必须明确地写出可能的类型。我正在尝试做的事情,“magicTool”不知道类型,并且要找出,它可能需要递归迭代,我不知道如何正确地做到这一点。
-
如果你真的需要将所有函数存储在一个容器中,你可以使用类似
std::function<std::any(std::any)>的东西并将所有函数包装在一个 lambda 中,该 lambda 将any_cast传递给正确的参数元组并传递它到你的实际功能。 -
旁白:我在这里看不到
forward的意义。Callables[fId](*argsPack)还不够吗?
标签: c++ tuples mpi c++20 variant