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);
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*
.