C ++ 11: calculating variable type parameter types

Suppose you want to write a function that passes an opaque function descriptor of an unknown type (for example, the name of a structure containing a function with a consistent name) and forwards the arguments to that function.

In the non-invariant case, considering one-parameter functions for simplicity, there are two ways to do this: you can let the forwarding function accept an argument of an arbitrary type and try to call the forwardee function with it, and the compiler will complain when the template is expanded if it turns out to be incompatible; or you can use decltype and various other mechanisms to find out what type of parameter the forwardee function expects, and explicitly requires an argument of that type. I don’t know if there is an acceptable terminology for them, so I’ll call them “passing” and “ahead”.

The pass-through method directly generalizes functions with an arbitrary number of parameters, but the leading edge method does not.

#include <iostream> template<typename T, typename Arg> void pass_through_1(Arg arg) { T::f(arg); } template<typename T> struct arg_of_1; template<typename Ret, typename Arg> struct arg_of_1<Ret (Arg)> { typedef Arg type; }; template<typename T> void up_front_1(typename arg_of_1<decltype(T::f)>::type arg) { T::f(arg); } template<typename T, typename... Args> void pass_through_var(Args... args) { T::f(args...); } template<typename T> struct args_of_var; template<typename...> struct type_list; template<typename Ret, typename... Args> struct args_of_var<Ret (Args...)> { // typedef Args... type; // can't do this typedef type_list<Args...> type; }; // template<typename T> // void up_front_var(typename args_of_var<decltype(T::f)>::type... args) // can't do this // { // T::f(args...); // } struct test { static void f(int x) { std::cout << x*9 << std::endl; } }; int main(int, char**) { pass_through_1<test>(7); up_front_1<test>(8); pass_through_var<test>(9); // up_front_var<test>(10); return 0; } 

The problem is that parameter packages cannot be free, only as arguments to the template, and if you wrap them in a covering template, then there is no way to unpack and unpack them in place, only according to the matching template.

Up has some advantages, such as improved self-documentation and better support for type inference (up_front <T> itself can be declared). Is there a way to make it work in the variational case? (Of course, you can use std :: tuple, but this is pretty unsatisfactory.)

+4
source share
2 answers

There is nothing like writing down a question so that you understand the answer.

Here is one way:

 template<typename T, typename Args = typename args_of_var<decltype(T::f)>::type> struct up_front_var; template<typename T, typename... Args> struct up_front_var<T, type_list<Args...>> { static void forward(Args... args) { T::f(args...); } }; 

I don’t think there is a way to make a top-level function out of it (you are starting the original problem again), but this may not be so bad.

I will be glad to see other solutions.

+1
source

Perhaps I misunderstand the question, but you can always leave argument types and give the compiler their output.

 /* declare */ template<typename T, typename... Args> void up_front_var(Args... args) { T::f(std::forward<Args>(args)...); // need std::forward to correctly handle lvalue/rvalue references } /* an example */ class Test { public: static void f(const char* fmt, int a, int b); }; void Test::f(const char* fmt, int a, int b) { printf(fmt, a, b); } int main() { up_front_var<Test>("testing %u, %u", 1, 2); // no need to specify arguments here return 0; } 
0
source

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


All Articles