Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to retrieve the argument types from a (Functor member's) function signature for use in a template?

Assume you have a functor:

struct MyFunctor
{
    bool operator ()( int value )
    {
        return true;
    }
};

Is it possible to retrieve a functor's member's argument type for use within your template? The following is a use of this mythical functionality:

template < typename FunctorType >
bool doIt( FunctorType functor, typename FunctorType::operator()::arg1 arg )
{
    return functor( arg );
}

Is there a valid syntax that would substitute for my mythical FunctorType::operator()::arg1 ?

like image 598
Catskul Avatar asked Jul 12 '11 16:07

Catskul


2 Answers

If you know the item is a functor, then you can just grab its operator(), like so:

#include <iostream>

template <unsigned Idx, typename... T>
struct pick
{
    static_assert(Idx < sizeof...(T), "cannot index past end of list");
};

template <typename T, typename... TRest>
struct pick<0U, T, TRest...>
{
    typedef T result;
};

template <unsigned Idx, typename T, typename... TRest>
struct pick<Idx, T, TRest...>
{
    typedef typename pick<Idx-1, TRest...>::result result;
};

template <typename Func>
struct func_traits;

template <typename TObj, typename R, typename... TArgs>
struct func_traits<R (TObj::*)(TArgs...)>
{
    typedef R result_type;

    template <unsigned Idx>
    struct argument
    {
        typedef typename pick<Idx, TArgs...>::result type;
    };
};

template <typename Func,
          typename Traits = func_traits<Func>,
          typename R = typename Traits::result_type,
          typename Arg0 = typename Traits::template argument<0>::type,
          typename Arg1 = typename Traits::template argument<1>::type
         >
void foo(Func f)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
};

struct thing
{
    void operator()(long, int*) { }
};

int main()
{
    foo(&thing::operator());
}

For me, that program prints out:

void foo(Func) [with Func = void (thing::*)(long int, int*), Traits = func_traits<void (thing::*)(long int, int*)>, R = void, Arg0 = long int, Arg1 = int*]

The key point being that Arg0 and Arg1 are long and int*, respectively.

like image 107
Travis Gockel Avatar answered Nov 15 '22 03:11

Travis Gockel


No there is not. The most elegant way to do this would be to either require your functors to provide a typedef for the argument-type, or to introduce a traits-class. The latter is useful if you want your template to work with functors and functions.

Alternatively, you can just make the argument type a second template parameter:

template < typename FunctorType, class ArgumentType >
bool doIt( FunctorType functor, ArgumentType arg )
{
    return functor( arg );
}

The compiler will still complain if ArgumentType does not match the type required by the functor.

like image 38
Björn Pollex Avatar answered Nov 15 '22 02:11

Björn Pollex