From the outside, I get an array of union structures with parameters that I want to pass to a previously saved function pointer.

In this connection, the question arose: is it possible by the template to create some proxy function, which, based on the argument list of the saved function pointer, unpack the original array?

Those. creating a template implementation of a class with a function pointer

//Ret(*foo)(Args... args) int foo(std::string, int) 

I will generate a method in it

 int bar(SomeThing* params) { return foo(Unpack<std::string>(params[0]), Unpack<int>(params[1])); } 

UPD: Example code with comments.

 template<typename Ret, typename... Args> class Interface : public Base { public: Interface(Ret(*function)(Args... args)) : function_(function) { } void Invoke(SomeThing* params, SomeThing* result) { Pack<Ret>(result, function_(Unpack(params)...)); } private: Ret(*function_)(Args... args); }; 

UPD 2: Intended Use.

 std::map<std::string, Base> map; map.insert("Test", new Interface<A(*)(B,C,D)>(&foo)); ... map["Test"]->Invoke(params, result); 
  • What does "... creating a template implementation of a class with a function pointer" mean? Also: void* params and then params[0] ? But the operator [] not applicable to a pointer of type void * . - AnT
  • 1. The function pointer is contained in the proxy class, from the common parent, in order to put everything in one container and call the virtual ProxyClass :: Invoke (void * params) heirs. Those. the phrase means: new ProxyClass <& foo> () you code 2. void is a pointer to an array of union structures, because within the framework of the question a specific type is not important, I substituted void, in fact there will be some SomeThing * - Gruberk
  • @AndreyZhbannikov No need to describe the code in words, give a minimal example demonstrating the intended use. And now you have a complete mess - then the function template, then the class template, then the virtual method. - VTT
  • ProxyClass Zhbannikov: Give the definition of ProxyClass . If you can do ProxyClass<&foo> , then it is completely incomprehensible how and where the unknown Ret and Args... will come from Args... - AnT
  • @AnT edited the question, a snag in this some unpack function or other way of passing parameters. I'm sorry, ProxyClass <Ret (*) (Args ...)> (& foo) of course. - Gruberk

2 answers 2

Given:

  • A structure that stores information in order to construct any necessary type:

     struct SomeThing{ //... }; 
  • A function that is capable of constructing any type required:

     template<class T> T unpack(const SomeThing &){ std::cout << typeid(T).name() << std::endl; return T(); } 

    By the way, if you add the template<class T> operator T() const method to the structure with data, then this function will be static_cast

  • The function to call:

     struct Bar{ }; int foo(int, double, const Bar &){ return 42; } 
  • Call

     int main(){ SomeThing params[10]; int answer = call(foo, params); std::cout << answer << std::endl; } 

Decision:

Using std::index_sequence_for generate a sequence of indices (1, 2, 3 ...) equal in length to the list of arguments. And with the help of the design

 foo(unpack<std::decay_t<Args>>(params[indexes])...); 

force the compiler to generate something like this:

 foo(unpack<std::decay_t<Arg1>>(params[1]), std::decay_t<Arg2>>(params[2]), ...); 

Full code:

 template<class Ret, class ...Args, std::size_t ...indexes> Ret call(Ret(*func)(Args...), SomeThing *params, const std::index_sequence<indexes...> &){ return func(unpack<std::decay_t<Args>>(params[indexes])...); } template<class Ret, class... Args> Ret call(Ret(*func)(Args...), SomeThing *params ){ return call(func, params, std::index_sequence_for<Args...>()); } 

In the code there is a risk to fly beyond the bounds of the array, which, in fact, is an indefinite behavior. In my example, the size of the array is known at compile time, so you can pass the array by reference, and screw static_assert inside the call . But in general, prams can be allocated in a heap, so I did not add this check.

An example of checking the output of the array:

 template<class Ret, class ...Args, std::size_t ...indexes, std::size_t size> Ret call(Ret(*func)(Args...), SomeThing (&params)[size], const std::index_sequence<indexes...> &){ static_assert(sizeof...(indexes) <= size, "array \"params\" is too small"); return func(unpack<std::decay_t<Args>>(params[indexes])...); } template<class Ret, class... Args, std::size_t size> Ret call(Ret(*func)(Args...), SomeThing (&params)[size]){ return call(func, params, std::index_sequence_for<Args...>()); } 

Such code is more secure, but it won't work with dynamic arrays.

    As I understood the problem is that having a parameter number retrieved from the serialization container (in your case - void *) is not enough. It is necessary that either the unpack functions are applied to the container in the specified order, or that unpack is aware of all other types stored in the container. 2nd approach is IMHO easier.

    For example, if your SomeThing` * is a pointer to the corresponding std :: tuple:

     typedef void SomeThing; template< typename ...T > std::tuple<T...>* unpack_tuple(SomeThing* p) { return static_cast< std::tuple<T...>* >(p); // Возможно, что-то более сложное } template< typename R, typename ... T , size_t ... i > R imple_invoke( R (*fn)(T...), std::tuple<T...>* p , std::integer_sequence<size_t, i...> ) { return fn( std::get<i>(*p)... ) ; } template< typename R, typename ... T > R invoke( R (*fn)(T...), SomeThing *p ) { return imple_invoke(fn, unpack_tuple<T...>(p), std::make_index_sequence<sizeof...(T)> () ); } 

    Your Invoke:

     void Invoke(SomeThing* params, SomeThing* result) { Pack<Ret>(result, invoke(function_, params) )); }