Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I wrote "foreach" implementation for std::tuple. Here it is:

#pragma once

#include <tuple>
/**
 * Callback example:

struct Call{
    float k=0;

    template<typename T, int Index>        // lambda function not efficient than this. Tested -O2 clang, gcc 4.8
    inline void call(T &&t){
        std::cout << t.h << " ; " << "id = " << Index << std::endl;
    }
};
*/


namespace TUPLE_ITERATOR{
    template<typename Tuple, int index, int size>
    struct LOOP{
        template <typename Callback>
        static inline void wind(Tuple&& tuple, Callback&& callback){
            callback.template call<decltype(std::get<index>(tuple)), index> (std::get<index>(tuple));
            LOOP<Tuple, index+1, size>::wind( std::forward<Tuple>(tuple), std::forward<Callback>(callback) );
        }
    };

    template<typename Tuple, int size>
    struct LOOP_BACK{
        template <typename Callback>
        static inline void wind_reverse(Tuple&& tuple, Callback&& callback){
            callback.template call<decltype(std::get<size>(tuple)), size>( std::get<size>(tuple) );
            LOOP_BACK<Tuple, size-1>::wind_reverse( std::forward<Tuple>(tuple), std::forward<Callback>(callback) );
        }
    };

    // stop specialization
    template<typename Tuple, int size>
    struct LOOP<Tuple, size, size> {
        template <typename Callback>
        static inline void wind(Tuple&& , Callback&& ){
            // end
        }
    };
    template<typename Tuple>
    struct LOOP_BACK<Tuple, -1>{
        template <typename Callback>
        static inline void wind_reverse(Tuple&& , Callback&& ){
            // end
        }
    };
}

template<typename Tuple, typename Callback>
static void inline iterate_tuple(Tuple&& tuple, Callback&& callback){
    TUPLE_ITERATOR::LOOP< Tuple, 0, std::tuple_size< typename std::decay<Tuple>::type >::value >
            ::template wind<Callback>( std::forward<Tuple>(tuple), std::forward<Callback>(callback) );
}

template<typename Tuple, typename Callback>
static void inline iterate_tuple_back(Tuple&& tuple, Callback&& callback){
    TUPLE_ITERATOR::LOOP_BACK< Tuple, std::tuple_size< typename std::decay<Tuple>::type >::value-1 >
            ::template wind_reverse<Callback>( std::forward<Tuple>(tuple), std::forward<Callback>(callback) );
}

// Call:
// iterate_tuple(Callback(), std::make_tuple(1,2,3,"asdaa"));

But I look how other folks do that, and I see they do this in another way. They get array of indexes, and then recursively call callback function. Is my implementation worse than that? I ask this, because if call tuple_iterator twice, with the same parameters, the compiler starts to use asm "calls". Look HERE, on the red.

void TUPLE_ITERATOR::LOOP<std::tuple<std::pair<int, Data>,
std::pair<int, Data> >, 0, 2>:
share|improve this question
    
I used to use recursion a lot with templates. But I have moved from recursion to using std::integer_sequence and std::tuple to get the equivalent of a loop (though underneath in the standard code it is still recursion it is not visible from my code and thus easier to read). –  Loki Astari May 22 at 19:33
    
So, does your implementation suffers from such "issue", like mine? –  tower120 May 22 at 19:37
    
See below. :-1) –  Loki Astari May 22 at 19:45
add comment

1 Answer

I used to use recursion a lot with templates. But I have moved from recursion to using std::integer_sequence and std::tuple to get the equivalent of a loop (from posting my template code here).

Though underneath in the standard code it is still recursion it is not visible from my code and thus easier to read.

I am not trying to implement exactly what you have.
But if you look at the code you can see what I am trying to achiece and may be able to apply this technique to your code (thus making it easier to read and thus maintain).

#include <tuple>
#include <iostream>
#include <utility>



// The object that defines the iteration.
// Notice the use of make_integer_sequence here (it returns integer_sequence type)
//
// This defines how we are going to iterate over the tuple T.
//    
template<typename C, typename T, typename Seq = std::make_integer_sequence<int, std::tuple_size<T>::value>>
struct TupleIterate;


// A partial specialization of the above.
// Here we convert the integer_sequence into a sequence of integers S
// We can use variable argument expansion to generate the code inline
// with this sequence.
//
template<typename C, typename T, int... S>
struct TupleIterate<C, T, std::integer_sequence<int, S...>>
{
    TupleIterate(C caller, T const& val)
    {
        // Make a tuple.
        // This takes a variable number of arguments and creates the appropriate
        // tuple. As we don't use the tuple we don't even bother to store it.
        //
        // Use Var-Arg expansion to call caller for each argument in T
        // The results of these called are passed to make_tuple()
        //
        std::make_tuple(caller(std::get<S>(val))...);
    }
};

//
// Function: To allow argument deduction
//           Pass the values as parameters and it creates the TupleIterator
//           defined above. Because it deduces the types of its arguments
//           we don't need to specify them.
template<typename C, typename T>
TupleIterate<C,T> tuple_iterate(C caller, T const& val)
{
    return TupleIterate<C,T>(caller, val);
}


// An example caller object.
// Just to show it printing.
struct Caller
{
    // It needs an operator() for each type in the tuple.
    // For ease of use I have templatized this.
    template<typename T>
    T operator()(T const& data)
    {
        std::cout << "Data: " << data << "\n";
        return data;
    }
};

int main()
{
    auto    val = std::make_tuple(1,2,"Hi there");

    tuple_iterate(Caller(), val);
}
share|improve this answer
    
It does not compile goo.gl/wGaNJ8 –  tower120 May 22 at 19:47
    
@tower120 std::integer_sequence is a C++14 feature, you will need to give the option -std=c++1y to your compiler. Also, only the most recent compiler (often trunk versions) implement it. Choose GCC 4.9. –  Morwenn May 22 at 19:49
    
On the other hand, you can find several standard-compliant implementations on the internet that you can copy-paste meanwhile :) –  Morwenn May 22 at 19:49
    
g++ -std=c++1y call.cpp –  Loki Astari May 22 at 19:50
    
@LokiAstari Look here goo.gl/Eixw1v. At the bottom of assembly output 'TupleIterate<Caller, std::tuple<int, int, char const*>, ' Instead of proceed to put std::cout call's, compiler replace them with 2 TupleIterate calls. I mean, now its not plain list of std::cout calls (like it was with single tuple_iterate call). This is that issue, which have my own implementation. –  tower120 May 22 at 19:56
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.