函数的矢量,不同的签名和参数



我正在开发一个可以使用MPI的工具,该工具将使用多个函数,每个函数可能彼此完全不同,比如签名和参数数量。C++版本不需要最低版本,我想这将与最新版本一起编译。

这个想法是,我将推送一些参数和一个函数ID,并最终序列化它们以通过MPI传递。它将由另一个进程接收,并使用一些反序列化程序来构建一个元组或参数包,该元组或参数组将传递给函数#ID。

我想创建一个函数向量,这样使用这个#ID(本质上是向量中的索引(,我可以选择相应的函数并传递前面提到的元组。

我的工具将接收这个函数向量,然后在编译时知道它们的类型,在编译时也知道元组类型,但要使用的函数和元组将取决于接收到的#ID。

我尝试过这样的std::变体,但我不知道如何使它工作,因为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)

您可以使用std::apply将元组作为参数传递给函数。

为了存储函数,您需要某种类型的擦除。在本例中,我选择了std::any

为了用ID存储函数,我使用了std::map。

#include <iostream>
#include <functional>
#include <any>
#include <map>
#include <tuple>
int foo(int val) {
return val;
}
float bar(float val1, int val2) {
return val1 * val2;
}
void zor(int i) {
std::cout << i << 'n';
}
struct FuncCollection {
std::map<int, std::function<std::any(std::any)>> funcMap;
template <typename Ret, typename... Args>
void addFunc(int id, Ret (*fPtr)(Args...)) {
funcMap[id] = [=](std::any args) -> std::any {
auto tuple = std::any_cast<std::tuple<Args...>>(args);
if constexpr(std::is_same_v<Ret, void>) {
std::apply(fPtr, tuple);
return 0;
} else {
return std::apply(fPtr, tuple);
}
};
}
template <typename... Args>
auto callFunc(int id, std::tuple<Args...> args) {
return funcMap[id](args);
}
};
int main()
{
FuncCollection fc;
fc.addFunc(1, foo);
fc.addFunc(2, bar);
fc.addFunc(3, zor);
std::tuple<int> p1{1};
std::tuple<float, int> p2{3.14, 2};
std::tuple<int> p3{5};
auto r1 = fc.callFunc(1, p1);
auto r2 = fc.callFunc(2, p2);
fc.callFunc(3, p3);
std::cout << std::any_cast<int>(r1) << ' ' << std::any_cast<float>(r2) << 'n';
}

这只是一个例子,尤其缺乏足够的错误检查。std::any_cast将对无效的强制转换引发异常。

相关内容

  • 没有找到相关文章

最新更新