How to Get the Argument Types of a Function Pointer in a Variadic Template Class

How do I get the argument types of a function pointer in a variadic template class?

You can write function_traits class as shown below, to discover the argument types, return type, and number of arguments:

template<typename T> 
struct function_traits;

template<typename R, typename ...Args>
struct function_traits<std::function<R(Args...)>>
{
static const size_t nargs = sizeof...(Args);

typedef R result_type;

template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};

Test code:

struct R{};
struct A{};
struct B{};

int main()
{
typedef std::function<R(A,B)> fun;

std::cout << std::is_same<R, function_traits<fun>::result_type>::value << std::endl;
std::cout << std::is_same<A, function_traits<fun>::arg<0>::type>::value << std::endl;
std::cout << std::is_same<B, function_traits<fun>::arg<1>::type>::value << std::endl;
}

Demo : http://ideone.com/YeN29

How to extract the arguments of a function pointer as type parameter pack?

Both the manual upcast and extracting the parameter types should be
unnecessary.

You can just do this:

template <typename T, typename M, typename ...P>
void invoke(T &&object, M member, P &&... params)
{
(std::forward<T>(object).*member)(std::forward<P>(params)...);
}

Template parameter - function pointer with variadic arguments

You could do the following:

template<typename T, T> struct C;

template<typename T, typename R, typename ...Args, R (T::*F)(Args...)>
struct C<R (T::*)(Args...), F> {

R operator()(T &obj, Args &&... args) {
return (obj.*F)(std::forward<Args>(args)...);
}

};

and then in your program:

struct A {
int foo(int i) { return i; }
};

int main() {
C<int(A::*)(int), &A::foo> c;
A a;
std::cout << c(a, 42) << std::endl;
}

Live Demo

c++ Extract parameter type list from function pointer

The compiler say the you can't define a single type InputArgs

typedef Args InputArgs;

given that Args is a variadic list.

Maybe you can define a type base on a tuple

using InArgsTuple = std::tuple<Args...>;

so you can extract the single types in Args... using std::tuple_element

So, with a little template meta-programming, you should be able to write something as

using TplT = MemberFuncArgs<decltype(&TestAppObject::TestMethod)>::InArgsTuple;

std::function<void(TestAppObject*, typename std::tuple_element<Is, TplT>::type ...)>
func = &TestAppObject::TestMethod;

assuming that Is... is a variadic sequence of template integer values from zero to sizeof...(Args)-1.

The following is a full compiling C++20 example

#include <tuple>
#include <functional>

struct TestAppObject
{
int TestMethod (char, short, int, long, long long)
{ return 0; }
};

template <typename T>
struct MemberFuncArgs;

template <typename RT, typename Owner, typename ... Args>
struct MemberFuncArgs<RT(Owner::*)(Args...)>
{
static constexpr std::size_t ArgCount = sizeof...(Args);

using ReturnType = RT;
using InArgsTuple = std::tuple<Args...>;
};

int main()
{
using MFA = MemberFuncArgs<decltype(&TestAppObject::TestMethod)>;
using FunT = decltype([]<std::size_t ... Is>(std::index_sequence<Is...>)
-> std::function<void(TestAppObject*,
typename std::tuple_element<Is, MFA::InArgsTuple>::type ...)>
{ return {}; }
(std::make_index_sequence<MFA::ArgCount>{}));

FunT func = &TestAppObject::TestMethod;
}

If you can't use C++20 (so no template lambda and no lambda in not evaluated context) you can substitute the lambda with a traditional template function, only declared (because is used only inside decltype().

The following is a full compiling C++14/C++17 example.
#include
#include

struct TestAppObject
{
int TestMethod (char, short, int, long, long long)
{ return 0; }
};

template <typename T>
struct MemberFuncArgs;

template <typename RT, typename Owner, typename ... Args>
struct MemberFuncArgs<RT(Owner::*)(Args...)>
{
static constexpr std::size_t ArgCount = sizeof...(Args);

using ReturnType = RT;
using InArgsTuple = std::tuple<Args...>;
};

template <typename MFA, std::size_t ... Is>
std::function<void(TestAppObject*,
typename std::tuple_element<Is, typename MFA::InArgsTuple>::type ...)>
extra_function (std::index_sequence<Is...>);

int main()
{
using MFA = MemberFuncArgs<decltype(&TestAppObject::TestMethod)>;
using FunT = decltype(extra_function<MFA>
(std::make_index_sequence<MFA::ArgCount>{}));

FunT func = &TestAppObject::TestMethod;
}

Variadic template with member function pointer as parameter

I assume you call the function like this:

int main()
{
int i; char c;
objectsDo(&Object::close, i, c);
}

The problem is that the template arguments are deduced inconsequently:

template <typename ...Args_t>
bool objectsDo(bool (Object::*func)(Args_t...), Args_t&&... args)

Args_t is deduced as int, char for the first parameter and int&, char& for the second. That is because of the internal working of universal references: It works with reference collapsing.

Use another parameter-pack for the trailing parameters :

template <typename ...Args_t, typename... Args>
bool objectsDo(bool (Object::*func)(Args_t...), Args&&... args)
{ /* […] */ }

Or make the trailing parameter a non-deduced context:

template <typename T> struct identity {using type = T;};
template <typename T>
using identity_t = typename identity<T>::type;

template <typename ...Args_t>
bool objectsDo(bool (Object::*func)(Args_t...), identity_t<Args_t>... args){
// […]
}

Pointers to member as variadic template parameters

In C++14, you can use another level of indirection to do that:

struct A {
int a;
float b;
};

template<typename... T>
struct Bar {
template <T A::*... params>
struct Foo {
void Bar(A *obj) {
void *members[] { (&(obj->*params))... };
// ... do something ...
(void)members;
}
};
};

int main() {
A a;

Bar<int, float>::Foo<&A::a, &A::b> foo;
foo.Bar(&a);
}

auto keyword (introduced with the C++17 for non-type template parameters, as you mentioned) solves more or less this kind of issues. Think of std::integral_constant and how would it be more user-friendly if you hadn't to specify each time the type as the first argument...

Getting return type of a function pointer in parameter pack and saving it as concatenated tuple with other args

You're evaluating the metafunction too eagerly. The result_of part is evaluated for the int too.

You should rearrange the conditional such that what you're selecting is a metafunction instead of a type, and then evaluate that:

template <typename T> struct identity { using type = T; };

template <typename Arg>
using maybe_eval = typename std::conditional_t<
std::is_pointer<Arg>::value && std::is_function<typename std::remove_pointer<Arg>::type>::value,
std::result_of<Arg&&()>,
identity<Arg>
>::type;

using pattern_t = tuple_cat_t<std::tuple<maybe_eval<Args>>...>;

Note that I'm using both conditional_t and type

Member function pointer wrapper using variadic template (gcc, clang)

When you explicitly specify the types, a parameter pack is greedy. &Foo::bar will be parsed as part of typename ...Args, which causes the error.

The correct way to write this is to put it in the function parameter list, instead of a non-type template parameter.

template< class T, typename ...Args>
void member_dispatch(Args&&... args, void(T::*Member)(Args...), void* userdata)
{
T* obj = static_cast<T*>(userdata);
(obj->*Member)(std::forward<Args>(args)...);
}

int main()
{
Foo foo;
member_dispatch<Foo, int>(1, &Foo::bar, &foo);
return 0;
}

A Better Way:

It would be better to take advantage of C++'s template argument deduction. But here you doesn't put the parameter pact at the end of a function parameter list, which is a non-deduced context. So I suggest you re-order it, so that you don't need to specify the template argument:

template<class T, class K, typename ...Args>
void member_dispatch(K T::*ptr, void* userdata, Args&&... args)
{
T* obj = static_cast<T*>(userdata);
(obj->*ptr)(std::forward<Args>(args)...);
}

int main()
{
Foo foo;
member_dispatch(&Foo::bar, &foo, 1);
return 0;
}

Function pointer with variadic template arguments

First, we need a tag and a series of functions to get values from objects based on their types. Simple enough.

template<class T> struct typetag {};

const int& get_type_from_class(const Object* o, typetag<int>) {return o->value;}
const double& get_type_from_class(const Object* o, typetag<double>) {return o->rating;}
const char& get_type_from_class(const Object* o, typetag<char>) {return o->letter;}
const long& get_type_from_class(const Object* o, typetag<long>) {return o->tag;}

The next part is that we need to get types from a list of parameters based on their types, and the first parameter is the default to return if no parameters match. Also not insanely difficult. There's the recursive mismatch case, the recursive match case, and the last-is-match case. Though this appears to have a fair amount of recursion, even the simplest optimizer should be able to inline this to optimal assembly. For reasons I don't understand, these have to be in this exact order.

template<class T> 
const T& get_T_by_type(const T& def)
{return def;}

template<class T, class...pRest>
const T& get_T_by_type(const T& def, const T& returnme, const pRest&...rest)
{return returnme;}

template<class T, class p0, class...pRest>
const T& get_T_by_type(const T& def, const p0& discard, const pRest&...rest)
{return get_T_by_type(def, rest...);}

Finally, we call the function. For each ARGS1, we call get_T_by_type to get the ARGS2 of the same type, and as the default we use get_type_from_class to pass the existing value in the class.

template <typename RET, typename... ARGS1, typename... ARGS2>
void Mediator::change (Object* o, RET (Object::*f)(ARGS1...), const ARGS2&... args) {
for (Object* x : objects) {
(x->*f)(
get_T_by_type(get_type_from_class(o, typetag<ARGS1>{}),args...) //pass all args2
... //pass one of that for each args1
);
}
}

Note that I've changed the return type to void, since you're delegating to calling multiple functions. Alternatively, you could return a vector of the return results.

http://coliru.stacked-crooked.com/a/36afa072711b0655



Related Topics



Leave a reply



Submit