将std :: function代理到需要参数数组的C函数

时间:2014-12-17 07:01:12

标签: c++ c++11 binding variadic-templates std-function

我正在处理提供此形式钩子的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

1 个答案:

答案 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);
}

在上面的示例中,请考虑fstd::function,并且arityf的一种方式,您可以通过这种方式获得std::index_sequence编译时间。

注意:我使用的是C ++ 14 std::make_index_sequenceindices但是如果您需要使用C ++ 11代码,您仍然可以使用手工制作的等价物,就像你所链接的旧问题中的make_indiceslib::Integer一样。


后果:关于real code的问题,当然比上面复杂一点。扩展机制的设计使得每次调用扩展函数时,C API(lib::StringapplyFunc等...上方的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上创建的适当类型(StringEngine&等...的实例调用它来调用它带有REBVAL*和{{1}}的苍蝇。