Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #37156

Re: auto i:v as iterator

From "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Newsgroups comp.lang.c++
Subject Re: auto i:v as iterator
Date 2015-07-17 23:18 +0200
Organization A noiseless patient Spider
Message-ID <mobrcl$qbj$1@dont-email.me> (permalink)
References <20150716103906.1f3f26b3@delligoertz.goertz.local>

Show all headers | View raw


On 16-Jul-15 10:39 AM, Ralf Goertz wrote:
>
> is it possible to have the new auto syntax for loops and get an iterator
> instead of a copy or a reference to the stored object itself? I would
> love to use the new syntax, but often I need to know what number in a
> vector the current object is. The following program doesn't compile.
>
> #include <vector>
> #include <iostream>
>
> int main() {
>      std::vector<int> v={47,11};
>      for (auto i:v) {
>          //std::cout<<i<<std::endl;
>          std::cout<<distance(i,v.begin())<<endl;
>      }
> 	return 0;
> }

In Python there is a built-in function `enumerated` that produces a 
corresponding sequence of (index, value) pairs:

     raw_array = [1.2, 3.4, 5.6]
     for x in enumerate( raw_array ):
         print( "%d -> %g" % (x[0], x[1]) )

How to do that in C++?

Well, Öö Tiib has already mentioned the Boost Range library's `indexed`, 
and I would guess the Python `enumerate` facility served as inspiration 
for that.

If you don't want a dependency on Boost, or if you just don't want the 
over-engineered custom operator| thing, then it's not hard to do this 
yourself, e.g. like the following  --  I wrote this code in response to 
your posting, so it's not been tested more than the little program:

<file "Indexer.hpp">
#pragma once
// Copyright (c) 2015 Alf P. Steinbach

#include <iterator>         // std::iterator_traits
#include <stddef.h>         // ptrdiff_t
#include <type_traits>      // std::remove_reference
#include <utility>          // std::pair, std::declval, std::begin, 
std::end, std::move

namespace progrock{ namespace cppx{
     using std::begin;
     using std::declval;
     using std::end;
     using std::iterator;
     using std::iterator_traits;
     using std::move;
     using std::pair;
     using std::random_access_iterator_tag;
     using std::remove_reference;

     using Index = ptrdiff_t;

     template< class Underlying_iter_tp, class Index_tp = Index >
     class Indexer
     {
     public:
         using Underlying_iter = Underlying_iter_tp;
         using Index = Index_tp;

         using Item = typename remove_reference<decltype(( 
*declval<Underlying_iter>() ))>::type;

         struct Numbered_item
             : pair<Index, Item*>
         {
             using pair<Index, Item*>::pair;

             auto index() const -> Index { return first; }
             auto value() -> Item& { return *second; }
             auto value() const -> Item const& { return *second; }
         };

     private:
         Underlying_iter     under_begin_;
         Underlying_iter     under_end_;

         class Iter
             : public iterator<
                 typename 
iterator_traits<Underlying_iter>::iterator_category,
                 Numbered_item,
                 typename iterator_traits<Underlying_iter>::difference_type,
                 typename iterator_traits<Underlying_iter>::pointer,
                 typename iterator_traits<Underlying_iter>::reference
                 >
         {
         friend class Indexer;
         private:
             Underlying_iter     it_;
             Numbered_item       item_;

             Iter( Underlying_iter it )
                 : it_( move( it ) )
                 , item_( 0, &*it )      // TODO: Check validity
             {}

         public:
             friend
             auto operator<( Iter const& a, Iter const& b )
                 -> bool
             { return (a.it_ < b.it_); }

             friend
             auto operator<=( Iter const& a, Iter const& b )
                 -> bool
             { return (a.it_ <= b.it_); }

             friend
             auto operator==( Iter const& a, Iter const& b )
                 -> bool
             { return (a.it_ == b.it_); }

             friend
             auto operator>=( Iter const& a, Iter const& b )
                 -> bool
             { return (a.it_ >= b.it_); }

             friend
             auto operator>( Iter const& a, Iter const& b )
                 -> bool
             { return (a.it_ > b.it_); }

             friend
             auto operator!=( Iter const& a, Iter const& b )
                 -> bool
             { return (a.it_ != b.it_); }

             auto operator++()
                 -> Iter&
             {
                 ++it_;
                 ++item_.first; item_.second = &*it_;
                 return *this;
             }

             auto operator++( int )
                 -> Iter
             {
                 Iter original = *this;
                 ++*this;
                 return original;
             }

             auto operator*()
                 -> Numbered_item const&
             { return item_; }
         };

     public:
         auto begin() -> Iter { return Iter( under_begin_ ); }
         auto end() -> Iter { return Iter( under_end_ ); }

         Indexer( Underlying_iter const begin_iter, Underlying_iter 
const end_iter )
             : under_begin_( begin_iter )
             , under_end_( end_iter )
         {}
     };

     template< class Collection_tp >
     auto indexed( Collection_tp& c )
         -> Indexer<decltype( begin( c ) )>
     { return Indexer<decltype( begin( c ) )>( begin( c ), end( c ) ); }
}}  // namespace progrock::cppx
</file>

<file "main.cpp">
#include "Indexer.hpp"
using namespace progrock;
using cppx::indexed;

#include <iostream>
#include <array>
#include <list>
#include <vector>
using namespace std;

template< class Collection >
void display( Collection const& c )
{
     for( auto const item : indexed( c ) )
     {
         wcout << item.index() << L" → " << item.value() << endl;
     }
}

auto main() -> int
{
     display( array<double, 3>{1.2, 3.4, 5.6} );
     wcout << endl;
     display( vector<double>{1.2, 3.4, 5.6} );
     wcout << endl;
     display( list<double>{1.2, 3.4, 5.6} );
     wcout << endl;

     vector<double> v(5);
     for( auto item : indexed( v ) )
     {
         item.value() = 0.1*item.index();
     }
     display( v );
}
</file>

The little arrow in the output may be displayed incorrectly or may cause 
truncation of the output. One fix is to replace it with "->", which has 
only ASCII characters. Another fix is to configure the C++ i/o properly, 
which, sadly, is not done by default by current implementations; for 
code that does this see e.g.

     <url: 
http://stackoverflow.com/questions/30197758/how-can-i-make-unicode-iostream-i-o-work-in-both-windows-and-unix-land>

And if you want to display that little arrow with Python in Windows 
consoles, see e.g.

     <url: 
https://alfps.wordpress.com/2015/05/12/non-crashing-python-3-x-output-in-windows/>


Cheers & hth.,

- Alf

-- 
Using Thunderbird as Usenet client, Eternal September as NNTP server.

Back to comp.lang.c++ | Previous | Next — Previous in thread | Find similar | Unroll thread


Thread

auto i:v as iterator Ralf Goertz <me@myprovider.invalid> - 2015-07-16 10:39 +0200
  Re: auto i:v as iterator Bo Persson <bop@gmb.dk> - 2015-07-16 11:09 +0200
    Re: auto i:v as iterator Ralf Goertz <me@myprovider.invalid> - 2015-07-16 12:00 +0200
      Re: auto i:v as iterator Öö Tiib <ootiib@hot.ee> - 2015-07-16 05:17 -0700
        Re: auto i:v as iterator Ralf Goertz <me@myprovider.invalid> - 2015-07-16 15:47 +0200
          Re: auto i:v as iterator Öö Tiib <ootiib@hot.ee> - 2015-07-16 08:27 -0700
            Re: auto i:v as iterator Ralf Goertz <me@myprovider.invalid> - 2015-07-17 07:57 +0200
  Re: auto i:v as iterator bartekltg <bartekltg@gmail.com> - 2015-07-16 18:06 +0200
  Re: auto i:v as iterator "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2015-07-17 23:18 +0200

csiph-web