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

I am dealing with a C system that offers a hook of this form:

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

You can register EXTENSIONFUNCTION and the number of arguments it takes.

My idea was that I would make an Extension class to complete the extension. It could be built from std :: function (or any Callable, ideally, but let it just say that it now contains std :: function). And the extension takes parameter values ​​that complete NATIVEVALUE (but more). For example, I will automatically take care of the parameter counter using sizeof...(Ts) . It might look like this:

 Extension<lib::Integer, lib::String> foo = [](lib::Integer i, lib::String s) -> int { std::cout << i; std::cout << s; return 0; } 

The problem is that in order for the C library to register and call it, it wants an array-based interface: - /

I decided to try and get the compiler to write a small pad, but I see no way to do this. I can have the operator() variable in Extension and execute a run loop on NATIVEVALUE to get an array of Value []. But what should I do about it? I cannot call std :: function with it.

I think I need to create an EXTENSIONFUNCTION instance that calls my std :: function as a member of each Extension instance.

But basically I find myself against a wall where I have a variational template for expansion ... and then it seems to be "unable to get from here" in terms of accepting this NATIVEVALUE args[] and the ability to call the std :: function with them. If std :: function wants to be called using the std :: array of arguments, this will solve it, but of course, this is not how it works.

Is it possible to build a gasket of this type? The ugly thing I can do is just a proxy for another array, for example:

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

But it is not so ergonomic. This seems impossible without knowing the calling conventions and making some kind of built-in assemblies to process the parameters and use the CALL function (and even this will only work for functions, not Callables in general). But people here have proved the impossible before, usually by "what you don’t want, what you really want ..."


UPDATE: I just found this that seems promising ... I'm still trying to digest its relevance:

"unpacking" a tuple to call the corresponding function pointer

(Note: there are some cross-cutting issues in what I intend to do. Another point is type inference from lambdas. The answer here seems to be the best choice on this ... it seems to work, but I don’t know know if this is "kosher": Initialize a class containing std :: function with lambda )

+6
source share
1 answer

If I managed to reduce the problem to its simplest form, you will need a way to call std::function , using your argument from a fixed-size C-style array, without creating a run loop. Then these functions can solve your problem:

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

Here is an example that demonstrates how to use it:

 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 } 

Of course, with the signature int (*)(T args[]) , args is just T* , and you don't know its size at compile time. However, if you know the size of the compilation time from another place (for example, from std::function ), you can configure apply_from_array to manually provide information about the size of the compilation time:

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

And then use the following function:

 int c_function(NATIVEVALUE args[]) { return apply_from_array<arity>(f, args); } 

In the above example, consider that f is std::function and that arity is the arity of f that you managed to get one way or another at compile time.

NOTE: I used C ++ 14 std::index_sequence and std::make_index_sequence , but if you need your code to work with C ++ 11, you can still use manual equivalents like indices and make_indices in my old question, which you tied.


Consequences: the question of real code , it was, of course, a little more complicated than the above. The extension mechanism is designed so that every time the extension function is called, C ++ proxys over the C API ( lib::Integer , lib::String , etc.) are created on the fly and then passed to the user-defined function. This required a new applyFunc method in 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 performs the function of applyFunc calls with instances of the corresponding types ( Integer , String , etc.) on the fly from the base C API created on the fly using Engine& and a REBVAL* .

+2
source

Source: https://habr.com/ru/post/979759/


All Articles