views:

210

answers:

3

I wrote a class that wraps an iterator and returns transformed values on demand:

// iterator-wrapper.h
template<class Iter, class Val, class Fct>
class IteratorWrapper {
  Iter cur_;
  const Iter last_;
  const Fct fct_;
public:
  IteratorWrapper(Iter first, Iter last, const Fct fct)
    : cur_(first), last_(last), fct_(fct) 
  {}
  const Val Value() const {return fct_(*cur_);}
  void Next() {++cur_;}
  bool Done() const {return cur_ == last_;}
};

Now, a class can use it to return iterators over some functions of its data:

// mydata.h
#include <map>
#include "iterator-wrapper.h"

class MyData {

  struct GetFirst {
    template<class T1, class T2>
      const T1& operator()(const std::pair<T1,T2>& aPair) const {
      return aPair.first;
    }
  };
  struct GetSecond {
    template<class T1, class T2>
      const T2& operator()(const std::pair<T1,T2>& aPair) const {
      return aPair.second;
    }
  };

  typedef std::string Key;
  typedef int Val;
  typedef std::map<Key, Val> Map;
  typedef Map::const_iterator MapIter;
  Map m_;

public:

  typedef IteratorWrapper<MapIter, Key, GetFirst> KeysIter;
  typedef IteratorWrapper<MapIter, Val, GetSecond> ValuesIter;

  MyData() { // add some data
    m_["foo"] = 1;
    m_["bar"] = 2;
  }

  KeysIter GetKeys() const {
    return KeysIter(m_.begin(), m_.end(), GetFirst());
  }

  ValuesIter GetValues() const {
    return ValuesIter(m_.begin(), m_.end(), GetSecond());
  }
 };

And, here is an example usage:

#include <iostream>
#include "iterator-wrapper-data.h"

 int main() {
   MyData d;

   std::cout << "KEYS:" << std::endl;
   MyData::KeysIter kit = d.GetKeys();
   for(; !kit.Done(); kit.Next()){
     std::cout << kit.Value() << std::endl;
   }

   std::cout << "VALUES:" << std::endl;
   MyData::ValuesIter vit = d.GetValues();
   for(; !vit.Done(); vit.Next()){
     std::cout << vit.Value() << std::endl;
   }
   return 0;
 }

I have several questions about this:

  1. Is this a reasonable design, or could one do it more succinctly, maybe using STL or boost stuff? (I know there is a boost::iterator_facade but I found that code way more complicated than necessary and I'm not sure if it does exactly what I want here.)

  2. Why doesn't std::map contain something like that to begin with (I mean a keys() function that returns an iterator over keys, etc.)? (Or does it?)

  3. What is wrong with returning a reference, like const Val& Value() const (as opposed returning by value, as in the first listing above)?

+1  A: 

It doesn't seem unreasonable to me, but I don't think it's really necessary. There are select1st and select2nd, from the SGI STL Guide:

int main()
{
  map<int, double> M;
  M[1] = 0.3;
  M[47] = 0.8;
  M[33] = 0.1;

  transform(M.begin(), M.end(), ostream_iterator<int>(cout, " "),
            select1st<map<int, double>::value_type>());
  // The output is  1 33 47.
}

Oh, and of course, there are compose1 and compose2 when you want to do more than just feed them to some insert or output iterator directly.

wich
These are nice if you have them but are not part of the C++ standard.
Georg Fritzsche
Well, they are pretty ubiquitous, and of course easily provided if higher portability is required.
wich
+4  A: 

You were on the right track by taking a look at boost::iterator_facade, but you're even better off with boost::transform_iterator.

villintehaspam
+2  A: 

About your second question: the Boost RangeEx library (zip file) (not yet distributed with Boost but accepted for future inclusion) contains range adaptors for iterating over map keys and values:

int main() {

   std::map<int, std::string> m;
   m[1] = "one";
   m[2] = "two";

   std::cout << "KEYS:" << std::endl;
   BOOST_FOREACH(int k, m | boost::adaptors::map_keys) {
       std::cout << k << std::endl;
   }

   std::cout << "VALUES:" << std::endl;
   BOOST_FOREACH(std::string const & v, m | boost::adaptors::map_values) {
       std::cout << v << std::endl;
   }
   return 0;
}
Manuel