2

I'm maintaining a std::tuple of iterators to implement something like a zipped iterator.

template <typename... Args>
class ZipIterator {
public:
    typedef typename std::tuple< typename std::decay<Args>::type::iterator ... > IteratorTuple ;

    ZipIterator( const Args&... args ) : 
        tuple( args.begin() ... ){} ;

private:
    IteratorTuple tuple ;
} ;

template <typename... Args>
ZipIterator<Args...> zip( Args&&... args ){
    return ZipIterator<Args...>( args... ) ;    
}

Now I'd like to implement iterator operations like e.g. operator++. The basic idea being that ZipIterator::++ just ++ all of the iterators hosted in the tuple. What I have so far is this:

template<int ...>
struct sequence {};

template<int N, int ...S> 
struct gens : gens<N-1, N-1, S...> {};

template<int ...S> 
struct gens<0, S...>{ 
    typedef sequence<S...> type; 
};

template<typename... Args>
struct index_sequence {
    typedef typename gens<sizeof...(Args)>::type type ;
} ;

template <typename... Args>
class ZipIterator {
public:
    typedef typename std::tuple< typename std::decay<Args>::type::iterator ... > IteratorTuple ;
    typedef typename std::tuple< typename std::decay<Args>::type::iterator::value_type ... > ValueTuple ;
    typedef typename index_sequence<Args...>::type Sequence ;

    ZipIterator( const Args&... args ) : 
        tuple( args.begin() ... ){} ;

    ZipIterator& operator++(){
        increment_all( Sequence() ) ;
        return *this ;
    }

    ValueTuple operator*(){
        return deref( Sequence() ) ;    
    }

private:

    template <typename... Pack>
    void nothing( Pack... ){}

    template <int... S>
    void increment_all(sequence<S...>) {
        nothing( increment<S>()... ) ;    
    }

    template <int S>
    typename std::tuple_element<S,IteratorTuple>::type increment(){
        return ++std::get<S>(tuple) ;
    }

    template <int... S>
    ValueTuple deref( sequence<S...> ){
        return ValueTuple( *std::get<S>(tuple) ... ) ;    
    }

    IteratorTuple tuple ;
} ;

template <typename... Args>
ZipIterator<Args...> zip( Args&&... args ){
    return ZipIterator<Args...>( args... ) ;    
}

I'd like to draw attention to this part:

    template <typename... Pack>
    void nothing( Pack... ){}

    template <int... S>
    void increment_all(sequence<S...>) {
        nothing( increment<S>()... ) ;    
    }

    template <int S>
    typename std::tuple_element<S,IteratorTuple>::type increment(){
        return ++std::get<S>(tuple) ;
    }

So what I do now is return something from increment and then send this into the nothing function that does ... nothing. Is there another way, e.g. I've tried:

    template <int... S>
    void increment_all(sequence<S...>) {
        { increment<S>()... ; } ;    
    }

But I get:

file5e1e684094e9.cpp:51:29: error: expected ';' after expression
            { increment<S>() ... ; } ;
                            ^
                            ;
file5e1e684094e9.cpp:51:15: error: expression contains unexpanded parameter pack 'S'
            { increment<S>() ... ; } ;
              ^         ~
file5e1e684094e9.cpp:51:30: error: expected expression
            { increment<S>() ... ; } ;
                             ^

I also have:

    template <int... S>
    void increment_all(sequence<S...>) {
        std::make_tuple( increment<S>() ... ) ;
    }

But it seems like a waste as I'm not going to use the tuple at all. In fact, I'd like a solution where I can have increment return void.

edit:

Based on the various comments, I now have this, with some help from the preprocessor.

    #define DO_PREFIX(PREFIX) nothing( ( PREFIX std::get<S>(tuple) , 0 )... )
    #define DO_SUFFIX(SUFFIX) nothing( ( std::get<S>(tuple) SUFFIX , 0 )... )

    template <int... S>
    void increment_all(sequence<S...>) {
        DO_PREFIX(++) ;
    }

    template <int... S>
    void decrement_all(sequence<S...>) {
        DO_PREFIX(--) ;
    }

    template <int... S>
    void increment_all(int n, sequence<S...>) {
        DO_SUFFIX(+=n) ;
    }

    template <int... S>
    void decrement_all(int n, sequence<S...>) {
        DO_SUFFIX(-=n) ;
    }
Romain Francois
  • 17,432
  • 3
  • 51
  • 77
  • I think there was something related [in this answer](http://stackoverflow.com/a/20844582/596781). Generally, you cannot pack-expand statements, only certain expressions. So you can try `int dummy[] = { (increment(), 0)... };` or something like that. – Kerrek SB Jun 05 '14 at 08:29
  • [Live demo](http://ideone.com/sWUE50). – Kerrek SB Jun 05 '14 at 08:36
  • Thanks. Obviously I then get `unused variable 'dummy' [-Wunused-variable]` an TBH, I don't fond this much more elegant that my `nothing` function. – Romain Francois Jun 05 '14 at 08:56
  • `nothing((increment(), 0)...)` should do the trick. – Jarod42 Jun 05 '14 at 09:25
  • Accepting some help from the preprocessor, i.e. : `##define BAZINGA(WHAT) nothing( ( WHAT , 0 )... )` I can do: `BAZINGA( ++std::get(tuple) ) ;`. Too bad we can't pack expand statements ... – Romain Francois Jun 05 '14 at 09:55

1 Answers1

2

I would have implemented it with a enable_if like in this example:

#include <vector>
#include <tuple>
#include <type_traits>

template <typename... Args>
class ZipIterator {
  public:
    typedef typename std::tuple< typename std::decay<Args>::type::iterator ... > IteratorTuple ;
    typedef typename std::tuple< typename std::decay<Args>::type::iterator::value_type ... > ValueTuple ;

    ZipIterator( const Args&... args ) :
      tuple( args.begin() ... ){} ;

    ZipIterator& operator++(){
      increment_all<0>();
      return *this ;
    }

  private:
    template <unsigned Index>
    inline typename std::enable_if<(Index<sizeof...(Args)), void>::type increment_all(){
      ++std::get<Index>(tuple);
      increment_all<Index+1>();
    }

    template <unsigned Index>
    inline typename std::enable_if<(sizeof...(Args)<=Index), void>::type increment_all()
    {}

    IteratorTuple tuple ;
};

template <typename... Args>
ZipIterator<Args...> zip( Args&&... args ){
  return ZipIterator<Args...>( args... ) ;
}

int main() {
  std::vector<int> v1 {{1, 2, 3, 4}};
  std::vector<double> v2 {{1, 2, 3, 4}};

  auto zipped = zip(v1,v2);
  ++zipped;
  return 0;
}

The dummy trick works but looks inelegant to me.

Jean-Bernard Jansen
  • 7,544
  • 2
  • 20
  • 17
  • Thanks. Not sure I like using recursion for this when unpacking gets me close. As you can imagine, I would also imple `--`, `+=`, `-=` etc ... I would not want to write `increment_all()`, `increment_all(int)`, `decrement_all()`, `decrement_all(int)` with this approach. – Romain Francois Jun 05 '14 at 09:02