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

Hos*_*ork 6 c++ binding variadic-templates c++11 std-function

我正在处理一个提供这种形式的钩子的C系统:

int (*EXTENSIONFUNCTION)(NATIVEVALUE args[]);
Run Code Online (Sandbox Code Playgroud)

可以注册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;
    }
Run Code Online (Sandbox Code Playgroud)

问题是,为了让C库注册并调用它,它需要基于数组的接口.: - /

我开始尝试让编译器写一个小垫片,但我没有办法做到这一点.我可以operator()在Extension上有一个可变参数,并在NATIVEVALUE上执行运行时循环以获取Value []的数组.但是我该怎么做呢?我不能用它调用std :: function.

所以我似乎需要创建一个EXTENSIONFUNCTION实例,它调用我的std :: function,作为每个Extension实例的成员.

但基本上我发现自己靠墙,我有一个可变的模板类扩展...然后有一种"无法从这里到达"在获取这个NATIVEVALUE args[]并能够调用std ::与他们一起工作.如果std :: function愿意用std :: array参数调用,那将解决它,但当然这不是它的工作原理.

是否有可能建立这种类型的垫片?我能做的"丑陋"事情只是代理另一个数组,如:

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;
    }
Run Code Online (Sandbox Code Playgroud)

但这不符合人体工程学.似乎不可能,不知道调用约定和做某种内联汇编来处理参数和调用函数(甚至那只适用于函数,而不是一般的Callables).但是这里的人们已经证明了以前不可能实现的,通常是"那不是你想要的,你真正想要的是......"


更新:我刚刚发现这一点,这看起来很有希望......我仍在努力消化它的相关性:

"解包"一个元组来调用匹配的函数指针

(注意:在我的目标中有一些交叉问题.另一点是来自lambdas的类型推断.这里的答案似乎是最好的选择...它似乎有用,但我不知道如果它是"犹太":初始化类包含了拉姆达一个std ::功能)

Mor*_*enn 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());
}
Run Code Online (Sandbox Code Playgroud)

这是一个演示如何使用它的示例:

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
}
Run Code Online (Sandbox Code Playgroud)

当然,对于签名int (*)(T args[]),args只是 aT*并且您在编译时不知道它的大小。但是,如果您从其他地方(例如)知道编译时大小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());
}
Run Code Online (Sandbox Code Playgroud)

然后使用这样的函数:

int c_function(NATIVEVALUE args[])
{
    return apply_from_array<arity>(f, args);
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,考虑f是 an std::function,并且是您在编译时以某种方式设法获得arity的 arity 。f

注意:我使用了 C++14 std::index_sequence,但std::make_index_sequence如果您需要代码与 C++11 一起使用,您仍然可以使用手工制作的等效项,就像您链接的我的旧问题中的indices和。make_indices


结果:问题是关于真实代码的,它当然比上面复杂一点。扩展机制的设计使得每次调用扩展函数时,都会动态创建C API 之上的 C++ 代理( lib::Integer、等...),然后传递给用户定义的函数。lib::String这需要一种新方法,applyFuncExtension

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 {}
    );
}
Run Code Online (Sandbox Code Playgroud)

applyFunc接受该函数来调用 an 并通过使用 an和 a动态创建的底层 C API 动态地使用适当类型( Integer、等...)的实例来调用它。StringEngine&REBVAL*