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

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

我正在处理一个提供这种形式的钩子(Hook)的 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 库注册和调用它,它需要基于数组的接口(interface)。 :-/

我开始尝试让编译器编写一些填充程序,但我没有找到实现它的方法。我可以在 Extension 上有一个可变的 operator(),并在 NATIVEVALUE 上执行运行时循环以获得 Value[] 的数组。但是我该怎么办呢?我不能用它调用 std::function。

所以我似乎需要制作一个调用我的 std::function 的 EXTENSIONFUNCTION 实例,作为每个扩展实例的成员。

但基本上我发现自己遇到了一个问题,因为我有一个用于扩展的可变参数模板类...然后在采用此 NATIVEVALUE args[ 方面有点“无法从这里到达那里” ] 并能够使用它们调用 std::function。如果 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;
    }

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


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

"unpacking" a tuple to call a matching function pointer

( 注意:在我的目标中有一些交叉问题。另一点是来自 lambda 的类型推断。这里的答案似乎是最好的选择......它似乎有效,但我不知道它是否符合“犹太洁食”:Initialize class containing a std::function with a lambda )

最佳答案

如果我设法将问题简化为最简单的形式,您需要一种方法来调用 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::functionarityf 是您在编译时设法以某种方式获得的。

注意:我使用了 C++14 std::index_sequencestd::make_index_sequence 但如果您需要您的代码工作使用 C++11,您仍然可以在您链接的我的旧问题中使用手工制作的等价物,例如 indicesmake_indices


后果:问题是关于 real code ,当然比上面稍微复杂一点。扩展机制的设计使得每次调用扩展函数时,C++ 代理 C API(lib::Integerlib::String 等...)是动态创建的,然后传递给用户定义的函数。这需要一个新方法,Extension 中的 applyFunc:

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 接受函数来调用一个使用适当类型(IntegerString 等...)的实例调用它的函数来自使用 Engine&REBVAL* 即时创建的底层 C API。

关于c++ - 将 std::function 代理到需要参数数组的 C 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27519937/

相关文章:

c++ - 两个标准类型的两个模板函数

c++ - 如何检测非事件 Win32 应用程序中的最小化?

c++ - 无法从C++中的抽象类绑定(bind)函数

c# - 我可以将剪贴板作为命令参数发送吗?

c# - 打开和关闭绑定(bind)

c++ - 结构成员内存布局

c++ - 模板结构的静态常量成员的不同值

c++ - 如何在 C++ 中使用智能指针实现接口(interface)隔离原则?

c++ - 关于 std::tuple 对象上的 std::get() 返回类型的混淆

c++ - Q_PROPERTY NOTIFY 信号及其参数