我正在处理提供此形式钩子的C系统:
int (*EXTENSIONFUNCTION)(NATIVEVALUE args[]);
可以注册EXTENSIONFUNCTION及其所需的参数数量。
我的想法是,我要制作一个课程Extension
来结束扩展。它可以从std :: function (或任何Callable构建,理想情况下,但现在只是说它包含一个std :: function)。扩展名采用Value参数,包含NATIVEVALUE (但更大)。例如,我会使用sizeof...(Ts)
自动处理参数计数。它可能看起来像这样:
Extension<lib::Integer, lib::String> foo =
[](lib::Integer i, lib::String s) -> int {
std::cout << i;
std::cout << s;
return 0;
}
问题是,为了让C库注册并调用它,它需要基于数组的接口。 : - /
我开始尝试让编译器编写一个小垫片,但我没有看到一种方法。我可以在Extension上有一个变量operator()
,并在NATIVEVALUE上运行一个循环来获取一个Value []数组。但是我该怎么做呢?我无法用它调用std :: function。
所以我似乎需要创建一个EXTENSIONFUNCTION实例来调用我的std :: function,作为每个Extension实例的成员。
但基本上我发现自己靠墙,我有一个可变的模板类扩展...然后有一种&#34;无法从这里到达那里&#34;在获取此NATIVEVALUE args[]
并能够使用它们调用std :: function方面。如果std :: function愿意用std :: array参数调用,那就可以解决它,但当然这不是它的工作原理。
是否可以构建这种类型的垫片? &#34;丑陋&#34;我能做的就是代理另一个数组,比如:
Extension<2> foo =
[](lib::Value args[]) -> int {
lib::Integer i (args[0]);
lib::String s (args[1]);
std::cout << i;
std::cout << s;
return 0;
}
但这不符合人体工程学。似乎不可能,在不知道调用约定和做某种内联汇编的情况下处理参数并调用函数(甚至那只适用于函数,而不是一般的Callables)。但是这里的人们已经证明了以前不可能的事情,通常是通过&#34;那不是你想要的,你真正想要的是......&#34;
更新:我刚刚发现了这一点,这看起来很有希望......我还在尝试消化其相关性:
"unpacking" a tuple to call a matching function pointer
(注意:我打算做的事情有一些交叉问题。另一点是来自lambdas的类型推断。这里的答案似乎是最好的选择...它似乎有效,但我不知道它是否&#34; kosher&#34;:Initialize class containing a std::function with a lambda)
答案 0 :(得分:2)
如果我设法将问题简化为最简单的形式,您需要一种方法来调用std::function
从固定大小的C样式数组中获取其参数,而无需创建运行时循环。然后,这些功能可以解决您的问题:
template<std::size_t N, typename T, typename F, std::size_t... Indices>
auto apply_from_array_impl(F&& func, T (&arr)[N], std::index_sequence<Indices...>)
-> decltype(std::forward<F>(func)(arr[Indices]...))
{
return std::forward<F>(func)(arr[Indices]...);
}
template<std::size_t N, typename T, typename F,
typename Indices = std::make_index_sequence<N>>
auto apply_from_array(F&& func, T (&arr)[N])
-> decltype(apply_from_array_impl(std::forward<F>(func), arr, Indices()))
{
return apply_from_array_impl(std::forward<F>(func), arr, Indices());
}
以下是一个展示如何使用它的示例:
auto foo = [](int a, int b, int c)
-> int
{
return a + b + c;
};
int main()
{
Value arr[] = { 1, 2, 3 };
std::cout << apply_from_array(foo, arr); // prints 6
}
当然,使用签名int (*)(T args[])
,args
只是T*
,您在编译时也不知道它的大小。但是,如果您知道其他地方的编译时间大小(例如,来自std::function
),您仍然可以调整apply_from_array
来手动提供编译时大小信息:
template<std::size_t N, typename T, typename F, std::size_t... Indices>
auto apply_from_array_impl(F&& func, T* arr, std::index_sequence<Indices...>)
-> decltype(std::forward<F>(func)(arr[Indices]...))
{
return std::forward<F>(func)(arr[Indices]...);
}
template<std::size_t N, typename T, typename F,
typename Indices = std::make_index_sequence<N>>
auto apply_from_array(F&& func, T* arr)
-> decltype(apply_from_array_impl<N>(std::forward<F>(func), arr, Indices()))
{
return apply_from_array_impl<N>(std::forward<F>(func), arr, Indices());
}
然后使用这样的函数:
int c_function(NATIVEVALUE args[])
{
return apply_from_array<arity>(f, args);
}
在上面的示例中,请考虑f
是std::function
,并且arity
是f
的一种方式,您可以通过这种方式获得std::index_sequence
编译时间。
注意:我使用的是C ++ 14 std::make_index_sequence
和indices
但是如果您需要使用C ++ 11代码,您仍然可以使用手工制作的等价物,就像你所链接的旧问题中的make_indices
和lib::Integer
一样。
后果:关于real code的问题,当然比上面复杂一点。扩展机制的设计使得每次调用扩展函数时,C API(lib::String
,applyFunc
等...上方的C ++代理都会动态创建,然后传递给用户定义功能。这需要Extension
中的新方法template<typename Func, std::size_t... Indices>
static auto applyFuncImpl(Func && func,
Engine & engine,
REBVAL * ds,
utility::indices<Indices...>)
-> decltype(auto)
{
return std::forward<Func>(func)(
std::decay_t<typename utility::type_at<Indices, Ts...>::type>{
engine,
*D_ARG(Indices + 1)
}...
);
}
template <
typename Func,
typename Indices = utility::make_indices<sizeof...(Ts)>
>
static auto applyFunc(Func && func, Engine & engine, REBVAL * ds)
-> decltype(auto)
{
return applyFuncImpl(
std::forward<Func>(func),
engine,
ds,
Indices {}
);
}
:
applyFunc
Integer
使用该函数从在基础C API上创建的适当类型(String
,Engine&
等...的实例调用它来调用它带有REBVAL*
和{{1}}的苍蝇。