4

I want a class Foo to store a function pointer, which it got on construction, and to call this function at some point. I looked at these two questions for help:

And, based on the answers, came up with this code:

#include <functional>

template < int N, typename... ARGS >
struct apply_func {
   static void applyTuple( std::function<void(ARGS...)>& f,
                          const std::tuple<ARGS...>& t,
                          ARGS... args ) {
    apply_func<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... );
  }
};

template <typename... ARGS>
struct apply_func<0,ARGS...>
{
  static void applyTuple( std::function<void(ARGS...)>& f,
                          const std::tuple<ARGS...>& /* t */,
                          ARGS... args ) {
    f( args... );
  }
};

template < typename... ARGS >
void applyTuple( std::function<void(ARGS...)>& f,
                 std::tuple<ARGS...> const& t ) {
   apply_func<sizeof...(ARGS), ARGS...>::applyTuple( f, t );
}

template<typename... ARGS>
class Foo
{
    std::function<void(ARGS...)> m_f;
    std::tuple<ARGS...> *argument_pack;

    public:
    Foo(std::function<void(ARGS...)> f):m_f(f){}
    void operator()(ARGS... args);
    void run();

};

template<typename... ARGS>
void Foo<ARGS...>::operator()(ARGS... args)
{
    m_f(args...); // this works
}

template<typename... ARGS>
void Foo<ARGS...>::run()
{
    applyTuple<ARGS...>(m_f, *argument_pack); // this doesn't compile
}

void bar(int i, double d){}

int main(void)
{
    Foo<int,double> foo(bar);
    foo(1,1.0); // this works
    foo.run(); // this doesn't compile
}

If you compile with ´g++ -std=c++0x´ the next to last line will give this error:

    test.cc: In function ‘void applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&) [with ARGS = {int, double}]’:
test.cc:52:9:   instantiated from ‘void Foo<ARGS>::run() [with ARGS = {int, double}]’
test.cc:61:17:   instantiated from here
test.cc:27:8: error: no matching function for call to ‘apply_func<2, int, double>::applyTuple(std::function<void(int, double)>&, const std::tuple<int, double>&)’
test.cc:27:8: note: candidate is:
test.cc:6:19: note: static void apply_func<N, ARGS>::applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&, ARGS ...) [with int N = 2, ARGS = {int, double}]
test.cc:6:19: note:   candidate expects 4 arguments, 2 provided
test.cc: In static member function ‘static void apply_func<N, ARGS>::applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&, ARGS ...) [with int N = 2, ARGS = {int, double}]’:
test.cc:27:8:   instantiated from ‘void applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&) [with ARGS = {int, double}]’
test.cc:52:9:   instantiated from ‘void Foo<ARGS>::run() [with ARGS = {int, double}]’
test.cc:61:17:   instantiated from here
test.cc:9:9: error: no matching function for call to ‘apply_func<1>::applyTuple(std::function<void(int, double)>&, const std::tuple<int, double>&, const double&, int&, double&)’
test.cc:9:9: note: candidate is:
test.cc:6:19: note: static void apply_func<N, ARGS>::applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&, ARGS ...) [with int N = 1, ARGS = {}]
test.cc:6:19: note:   candidate expects 2 arguments, 5 provided
test.cc: In static member function ‘static void apply_func<N, ARGS>::applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&, ARGS ...) [with int N = 1, ARGS = {}]’:
test.cc:9:9:   instantiated from ‘static void apply_func<N, ARGS>::applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&, ARGS ...) [with int N = 2, ARGS = {int, double}]’
test.cc:27:8:   instantiated from ‘void applyTuple(std::function<void(ARGS ...)>&, const std::tuple<_Elements ...>&) [with ARGS = {int, double}]’
test.cc:52:9:   instantiated from ‘void Foo<ARGS>::run() [with ARGS = {int, double}]’
test.cc:61:17:   instantiated from here
test.cc:9:9: error: no matching function for call to ‘get(const std::tuple<>&)’
test.cc:9:9: note: candidates are:
/usr/include/c++/4.6/utility:133:5: note: template<long unsigned int _Int, class _Tp1, class _Tp2> typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/utility:138:5: note: template<long unsigned int _Int, class _Tp1, class _Tp2> const typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/tuple:531:5: note: template<long unsigned int __i, class ... _Elements> typename std::__add_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(std::tuple<_Elements ...>&)
/usr/include/c++/4.6/tuple:538:5: note: template<long unsigned int __i, class ... _Elements> typename std::__add_c_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(const std::tuple<_Elements ...>&)

What am I missing? Thanks!

Community
  • 1
  • 1
steffen
  • 8,572
  • 11
  • 52
  • 90

3 Answers3

2

I think the problem is that applyTuple takes a reference to a tuple as its second parameter:

template < typename... ARGS >
void applyTuple( std::function<void(ARGS...)>& f,
                 std::tuple<ARGS...> const& t ) {
   apply_func<sizeof...(ARGS), ARGS...>::applyTuple( f, t );
}

But, inside of run, you're passing in a pointer to a tuple, since argument_pack is a pointer to a tuple:

std::tuple<ARGS...> *argument_pack;

If you change the body of run to be

applyTuple<ARGS...>(m_f, *argument_pack);

then I think your problem should go away.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Wow, this is embarrassing... I am throwing around variadic templates and I am not able to dereference a pointer where I need to! Anyways, it is still not compiling. Thank you for pointing the error out! I updated the question. – steffen Feb 08 '12 at 09:18
  • as long as when you omit a small detail you get a nice clear and concise error message from the compiler telling you what you've done rather than some convoluted template mess to work through. – CashCow Feb 08 '12 at 18:32
0

You cannot say apply_func<N-1>::.... This will instantiate the structure with ARGS = {}, thus the "candidate expects 2 arguments, 5 provided" error.

Also, in applyTuple, you have used apply_func<sizeof...(ARGS), ARGS...>::applyTuple, which expects to accept 4 parameters: the function f, the tuple t, and the ARGS... args, but you cannot pass the args, thus the "candidate expects 4 arguments, 2 provided" error.

As an alternate solution, you could use my vtmp library and use

template<typename... ARGS>
void Foo<ARGS...>::run()
{
    utils::tuple_apply(*argument_pack, m_f);
}

(Note: requires g++ 4.7)

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
0

I solved it. It was the combination of three errors:

  1. the pointer error pointed out by user:templatetypedef
  2. I had to go back to the original version of apply tuple in this post: How do I expand a tuple into variadic template function's arguments?. I changed all the template parameters to ARGS... because for the tuple, function and class they are all the same. Honestly, I still don't understand why that shouldn't work...
  3. The function pointer had to be stored as such (void (*m_f) (ARGS...);) instead of a std::function<void(ARGS...)> object as I did. The constructor had to be changed accordingly, of course

Thank you for answering!

Community
  • 1
  • 1
steffen
  • 8,572
  • 11
  • 52
  • 90