tags:

views:

273

answers:

5

Hi,

I have a function named _push which can handle different parameters, including tuples, and is supposed to return the number of pushed elements.

For example, _push(5) should push '5' on the stack (the stack of lua) and return 1 (because one value was pushed), while _push(std::make_tuple(5, "hello")) should push '5' and 'hello' and return 2.

I can't simply replace it by _push(5, "hello") because I sometimes use _push(foo()) and I want to allow foo() to return a tuple.

Anyway I can't manage to make it work with tuples:

template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, typename std::enable_if<(N >= 1)>::type* = nullptr) {
 return _push<Args...,N-1>(t) + _push(std::get<N-1>(t));
}

template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, typename std::enable_if<(N == 0)>::type* = nullptr) {
 return 0;
}

Let's say you want to push a tuple<int,bool>. This is how I expect it to work:

  • _push<{int,bool}, 2> is called (first definition)
  • _push<{int,bool}, 1> is called (first definition)
  • _push<{int,bool}, 0> is called (second definition)

However with g++ 4.5 (the only compiler I have which supports variadic templates), I get an error concerning _push<Args...,N-1>(t) (line 3) saying that it couldn't find a matching function to call (without any further detail). I tried without the "..." but I get another error saying that the parameters pack is not expanded.

How can I fix this?

PS: I know that you can do this using a template struct (this is in fact what I was doing before), but I'd like to know how to do it with a function

PS 2: PS2 is solved, thanks GMan

+2  A: 
GMan
Don't take it bad, but I was precisely trying to avoid template structures
Tomaka17
@Tomaka: The structure is just to push some `T` on a lua stack. If you understand the code you can see you could replace the function call to `pFunc` with some specific function (aka removing the struct). Added.
GMan
A: 

I solved the problem with some hacks. Here is the code:

template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N >= 1)>::type* = nullptr) {
    return _push(t, static_cast<std::integral_constant<int,N-1>*>(nullptr)) + _push(std::get<N-1>(t));
}
template<typename... Args, int N = sizeof...(Args)>
int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N == 0)>::type* = nullptr) {
    return 0;
}

Don't hesitate to post if you find a better way

Tomaka17
A: 

If you want to iterate through a tuple with a function, you can do so with a (litte) bit of boilerplate. The idea is to build a variadic integer list corresponding to tuple indices, then use std::get to access values. Quickly:

template<int...> struct indices;

// constructs an index list for a tuple e.g. indices<0, 1, 2, 3> 
template<class ... Args> some_index_type make_indices();

Then you can expand a tuple like this:

template<class Args...> void foo(const std::tuple<Args...>& tup) {
    foo(tup, make_indices<Args...>());
}

template<class Args..., int...I> void foo(const std::tuple<Args...>& tup,
                                            indices<I...> ){
   bar( std::get<I>(tup)... );
}

This will expand tuple content and feed it to function bar.

Hope this helps :)

max
A: 

This is one of the simpler solutions I can think of. I tested it successfully with GCC 4.4:

#include <iostream>
#include <tuple>

template<class T>
void push(T x)
{
  using namespace std;
  cout << x << '\n';
}

template<int Remaining>
struct push_tuple_helper
{
  template<class... Args>
  static void doit(std::tuple<Args...> const& t)
  {
    push(std::get<sizeof...(Args)-Remaining>(t));
    push_tuple_helper<Remaining-1>::doit(t);
  }
};

template<>
struct push_tuple_helper<0>
{
  template<class... Args>
  static void doit(std::tuple<Args...> const& t) {}
};

template<class... Args>
void push(std::tuple<Args...> t)
{
  push_tuple_helper<sizeof...(Args)>::doit(t);
}

int main()
{
  using namespace std;
  push( 42 );
  cout << "---\n";
  push( "Hello World" );
  cout << "---\n";
  push( make_tuple(42,3.14,"foo") );
}
sellibitze
A: 

Instead of making one function do two different things, separate the concerns:

_push(value, ...); // using variadic templates for 1 to N values
_push_seq(sequence); // always requires a sequence, never a value

Then the problem simply no longer exists for _push! You have no ambiguity about whether to push one item containing multiple values (I'm unfamiliar with Lua, but I know it has a primary container class) or push multiple items from one sequence.

Renaming the functions may be helpful:

_append(value, ...); // _push(value, ...) above
_extend(sequence); // _push(sequence) above

For comparison, consider how std::vector always uses push_back for one item (_append) and insert for multiple items (_extend); it doesn't try to mix the two concepts.

Roger Pate