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

Proxying a std::function to a C function that wants an array of arguments

本文关键字:数组 参数 函数 std function 代理      更新时间:2023-10-16

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

int (*EXTENSIONFUNCTION)(NATIVEVALUE args[]);

可以注册一个 EXTENSION 函数及其所需的参数数量。

我的想法是,我会创建一个类Extension来包装扩展。 它将能够从一个std::function(或任何可调用的,理想情况下,但让我们说它现在包含一个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 库注册和调用它,它需要基于数组的接口。 :-/

我开始尝试让编译器编写一个小填充程序,但我看不到这样做的方法。 我可以在扩展上有一个可变参数operator(),并在 NATIVEVALUE 上执行运行时循环以获取 Value[] 数组。 但是我该怎么办呢? 我不能用它调用std::function。

因此,我似乎需要创建一个调用我的std::function的EXTENSIONFUNCTION实例作为每个扩展实例的成员。

但基本上我发现自己碰壁了,在那里我有一个用于扩展的可变参数模板类......然后是一种"无法从这里到达那里",就接受这个NATIVEVALUE args[]并能够与他们一起调用 std::function。 如果 std::function 愿意用 std::数组参数来调用,那将解决它,但当然这不是它的工作方式。

是否可以构建这种类型的垫片? 我能做的"丑陋"的事情就是代理到另一个数组,比如:

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;
    }

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


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

"解包"元组以调用匹配函数指针

(注:我打算做的事情有几个交叉问题。 另一点是来自 lambda 的类型推断。 这里的答案似乎是最好的选择...它似乎有效,但我不知道它是否是"犹太洁食":初始化包含 std::函数的类 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::functionarity是您在编译时设法以某种方式获得的f的稀有性。

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


后果:问题是关于真正的代码,当然比上面复杂一点。扩展机制的设计使得每次调用扩展函数时,C++ C API 之上的代理(lib::Integerlib::String 等(都会动态创建,然后传递给用户定义的函数。这需要一种新的方法,applyFunc 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函数使用具有Engine&REBVAL*动态创建的底层 C API 动态调用具有适当类型(IntegerString等(的实例调用它。