问题
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
回答1:
I don't have a compiler to test any of this, so you'll have to report any issues.
The following should allow you to iterate across a tuple calling a function. It's based off your logic, with a few minor changes. (N
is a std::size_t
, it's the first parameter to allow Args
(and Func
) to be deduced on further calls, it just calls some function instead of performing a specific task). Nothing too drastic:
namespace detail
{
// just to keep things concise and readable
#define ENABLE_IF(x) typename std::enable_if<(x)>::type
// recursive case
template <std::size_t N, typename... Args, typename Func>
ENABLE_IF(N >= 1) iterate(const std::tuple<Args...>& pTuple, Func& pFunc)
{
pFunc(std::get<N - 1>(pTuple));
iterate<N - 1>(pTuple, pFunc);
}
// base case
template <std::size_t N, typename... Args, typename Func>
ENABLE_IF(N == 0) iterate(const std::tuple<Args...>&, Func&)
{
// done
}
}
// iterate tuple
template <typename... Args, typename Func>
Func iterate(const std::tuple<Args...>& pTuple, Func pFunc)
{
detail::iterate<sizeof...(Args)>(pTuple, pFunc);
return pFunc;
}
Assuming that all works, you then just have:
struct push_lua_stack
{
// constructor taking reference to stack to push onto
// initialize count to 0, etc....
template <typename T>
void operator()(const T& pX)
{
// push pX onto lua stack
++count;
}
std::size_t count;
};
And lastly:
std::size_t pushCount = iterate(someTuple, push_lua_stack()).count;
Let me know if that all makes sense.
Since you seem to really be seriously against structs for some reason, just make a function like this:
template <typename T>
void push_lua(const T& pX)
{
// push pX onto lua stack
}
And change everything to specifically call that function:
namespace detail
{
// just to keep things concise and readable
#define ENABLE_IF(x) std::enable_if<(x)>::type* = nullptr
// recursive case
template <std::size_t N, typename... Args>
typename ENABLE_IF(N >= 1) iterate(const std::tuple<Args...>& pTuple)
{
// specific function instead of generic function
push_lua(std::get<N - 1>(pTuple));
iterate<N - 1>(pTuple);
}
// base case
template <std::size_t N, typename... Args, typename Func>
typename ENABLE_IF(N == 0) iterate(const std::tuple<Args...>&, Func&)
{
// done
}
}
// iterate tuple
template <typename... Args>
void _push(const std::tuple<Args...>& pTuple)
{
detail::iterate<sizeof...(Args)>(pTuple);
}
No idea why you'd avoid generic functionality though, or be so against structs.
Oh how nice polymorphic lambda's would be. Ditch the utility push_lua_stack
class and just write:
std::size_t count = 0;
iterate(someTuple, [&](auto pX)
{
// push onto lua stack
++count;
});
Oh well.
回答2:
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
回答3:
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.
回答4:
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 :)
回答5:
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") );
}
回答6:
Edit Generic for-each
template<size_t N>
struct for_each_impl
{
template<typename Func, typename Tuple>
void operator()(Func func, Tuple const& arg)
{
for_each_impl<N-1>()(func, arg );
return func( std::get<N-1>( arg ) );
}
};
template<>
struct for_each_impl<1>
{
template<typename Func, typename Tuple>
void operator()(Func func, Tuple const& arg)
{
func( std::get<0>( arg ) );
}
};
template<typename Func, typename ... Args>
void for_each( Func func, std::tuple<Args...>const& tup )
{
for_each_impl< sizeof...(Args)>()( func, tup );
}
Example Usage
struct printer {
ostream& out;
explicit printer( ostream& out=std::cout ) : out(out) { }
template<typename T>void operator()(T const&t) const { out<<t<<", "; }
};
cout << '[';
for_each( printer(cout), make_tuple(0,.1,"hello") );
cout << ']';
来源:https://stackoverflow.com/questions/3316573/c0x-iterating-through-a-tuple-with-a-function