Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve function arguments as a tuple in C++

Consider I have a function declarations like these:

void foo(int x, float y);

class X {
  void anotherFoo(double a, int c);
};

How can I get a tuple that corresponds to the function arguments? In the above case it would be:

boost::tuple<int, float>
boost::tuple<X*, double, int>

or even better with the result type as 0th element:

boost::tuple<void, int, float>
boost::tuple<void, X*, double, int>

I know that boost::function_types::parameter_types can do exactly this. However, I am interested in the principle of how it is implemented.

like image 794
Karel Petranek Avatar asked Nov 22 '11 09:11

Karel Petranek


People also ask

Is a function argument passed as list or tuple?

Due to the * prefix on the args variable, all extra arguments passed to the function are stored in args as a tuple.

Can a function take a tuple as an argument?

A tuple can be an argument, but only one - it's just a variable of type tuple . In short, functions are built in such a way that they take an arbitrary number of arguments. The * and ** operators are able to unpack tuples/lists/dicts into arguments on one end, and pack them on the other end.

Why is * args a tuple?

The *args thing returns tuple because of that, and if you really need a list, you can transform it with one line of code! So in general: It speeds up your program execution. You do not it to change the arguments. It is not that hard to change it's type.

How tuple can be passed to function?

This method of passing a tuple as an argument to a function involves unpacking method. Unpacking in Python uses *args syntax. As functions can take an arbitrary number of arguments, we use the unpacking operator * to unpack the single argument into multiple arguments.


2 Answers

You can get the tuple type corresponding to your argument types, like this:

template <typename R, typename... T>
std::tuple<T...> function_args(R (*)(T...))
{
    return std::tuple<T...>();
}

// get the tuple type
typedef decltype(function_args(foo)) FooArgType;
// create a default-initialised tuple
auto args = function_args(foo);

Is that what you want? Note you may need to add one or more overloads of function_args, eg. taking a class type param for class methods.

like image 75
Useless Avatar answered Oct 05 '22 22:10

Useless


Finally found a way how to do this in C++03 using partial specialization. A lot of overloads for different number of arguments and const/volatile functions are necessary but the idea is following:

/* An empty template struct, this gets chosen if the given template parameter is not a member function */
template <typename _Func>
struct MemberFunctionInfo {  };

/* Specialization for parameterless functions */
template <typename _Result, typename _Class>
struct MemberFunctionInfo<_Result (_Class::*) ()>  {
    typedef _Class class_type;
    typedef _Result result_type;
    typedef boost::tuple<> parameter_types;
    enum { arity = 0 };
};

/* Specialization for parameterless const functions */
template <typename _Result, typename _Class>
struct MemberFunctionInfo<_Result (_Class::*) () const> : MemberFunctionInfo<_Result (_Class::*) ()> { };

/* Specialization for functions with one parameter */
template <typename _Result, typename _Class, typename P0>
struct MemberFunctionInfo<_Result (_Class::*) (P0)>  {
    typedef _Class class_type;
    typedef _Result result_type;
    typedef boost::tuple<P0> parameter_types;
    enum { arity = 1 };
};

/* Specialization for const functions with one parameter */
template <typename _Result, typename _Class, typename P0>
struct MemberFunctionInfo<_Result (_Class::*) (P0) const> : MemberFunctionInfo<_Result (_Class::*) (P0)> { };

.
.
.

Example usage:

template <typename MemFunc>
int getArity(MemFunc fn)  {
  // Can also use MemberFunctionInfo<MemFunc>::parameter_types with boost::mpl
  return MemberFunctionInfo<MemFunc>::arity;
}

The above solution has some flaws. It doesn't handle function references, non-member functions or volatile/const volatile member functions but it's easy to account for these by adding more specializations.

For C++11, the approach mentioned by @Useless is way cleaner and should be preferred.

like image 41
Karel Petranek Avatar answered Oct 06 '22 00:10

Karel Petranek