函数向量、不同的签名和参数
Vector of functions, different signature and parameters
我正在开发一个可以与 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
我尝试了几个选项,但我总是遇到这样一个事实,即我需要事先知道数据类型,并且不能使用条件,因为编译器可以匹配一个函数并与其他函数一起抛出错误。
我该怎么做?
请在下面找到 python 我想用 C++ 做什么的片段。
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
将在无效转换时抛出异常。
我正在开发一个可以与 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
我尝试了几个选项,但我总是遇到这样一个事实,即我需要事先知道数据类型,并且不能使用条件,因为编译器可以匹配一个函数并与其他函数一起抛出错误。
我该怎么做?
请在下面找到 python 我想用 C++ 做什么的片段。
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
将在无效转换时抛出异常。