Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++0x: iterating through a tuple with a function

Tags:

c++

c++11

tuples

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

like image 641
Tomaka17 Avatar asked Feb 08 '26 04:02

Tomaka17


1 Answers

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.

like image 114
GManNickG Avatar answered Feb 12 '26 04:02

GManNickG



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!