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.
Due to the * prefix on the args variable, all extra arguments passed to the function are stored in args as a tuple.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With