46

This is a follow up of this problem: Generic functor for functions with any argument list

I have this functor class (full code see link above):

template<typename... ARGS>
class Foo
{
    std::function<void(ARGS...)> m_f;
public:
    Foo(std::function<void(ARGS...)> f) : m_f(f) {}
    void operator()(ARGS... args) const { m_f(args...); }
};

In operator() I can access the args... easily with a recursive "peeling" function as described in Stroustrup's C++11 FAQ

My problem is: I want to access the types of the arguments of f, i.e. ARGS..., in the constructor. Obviously I can't access values because there are none so far, but the argument type list is somehow burried in f, isn't it?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
steffen
  • 8,572
  • 11
  • 52
  • 90

1 Answers1

86

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

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Thank you @Nawaz, works so far. Nevertheless, would like to extract the "magic" out of this solution and put it into my code. I suppose typename std::tuple_element>::type is where it happens... How do I do that without having to declare another struct – steffen Jan 30 '12 at 15:03
  • @steffen: Do you have any problem in defining another struct which can be used in other situations as well? Also, putting all the code in just one class is not a good idea. Try to divide code into small working units. – Nawaz Jan 30 '12 at 15:11
  • I get your point. One last question is this piece of code from the boost library? function_traits sounds familiar ;) – steffen Jan 30 '12 at 15:15
  • @steffen: Boost might have similar functionality. I've not seen it. Actually this solution is copied from my another answer here : [How to get the number of arguments of `std::function`?](http://stackoverflow.com/questions/9044866/how-to-get-the-number-of-arguments-of-stdfunction) – Nawaz Jan 30 '12 at 15:29
  • Hi again @Nawaz! It seems not to work when fun is typed std::function inside my variadic template constructor. – steffen Jan 30 '12 at 16:11
  • 1
    @steffen: It will work. You must be doing something wrong. Better post the code at www.ideone.com so that I can see the error myself – Nawaz Jan 30 '12 at 16:16
  • 1
    @steffen: The problem is that when you use `ARG...`, you need to use `typename` and `template` keywords to disambiguate the situations. So instead of `function_traits::arg<0>::type`, you need to write `typename function_traits::template arg<0>::type`. See it works now : http://ideone.com/bysFS – Nawaz Jan 30 '12 at 17:17
  • I posted a follow-up here http://stackoverflow.com/questions/9077612/using-non-const-expression-as-template-parameter – steffen Jan 31 '12 at 10:05
  • 1
    @Nawaz hm, The Wayback Machine was no help in retrieving the contents of your dead "Demo" link. :( – Drew Dormann Apr 18 '20 at 16:43
  • Added to universal standard library. – Kalen Feb 05 '22 at 07:17