Boost logo

Boost :

Subject: [boost] [iterator] Silent-but-deadly bug in iterator_facade category
From: Gabriel Redner (gredner_at_[hidden])
Date: 2013-05-06 16:17:58


Hi folks,

I've stumbled across a nasty problem with iterator_facade in which
code which appears correct and compiles fine can cause runtime
problems. Consider an iterator_facade which just wraps a random
access iterator and applies a transformation to it:

char transform(int i) { return 'a'; }

struct my_iterator
  : public boost::iterator_facade<my_iterator,
                                  char, // value_type
                                  std::random_access_iterator_tag, // ***
                                  char> // reference
{
  my_iterator(const std::vector<int>::iterator& it) : _it(it) {}

  char dereference() const { return transform(*_it); }
  bool equal(const my_iterator& other) const { return _it == other._it; }
  void increment() { ++_it; }
  void decrement() { --_it; }
  void advance(int n) { std::advance(_it, n); }
  int distance_to(const my_iterator& other) const { return other._it - _it; }

private:
  std::vector<int>::iterator _it;
};

Now if I have an STL-style function which is overloaded on the
iterator category, clearly the random-access version will be selected.

template <typename TIterator>
void funcImpl(TIterator it, std::input_iterator_tag)
{ std::cerr << "Running func treating 'it' as input iterator\n"; }

template <typename TIterator>
void funcImpl(TIterator it, std::random_access_iterator_tag)
{ std::cerr << "Running func treating 'it' as random access iterator\n"; }

template <typename TIterator>
void func(TIterator it)
{ funcImpl(it, typename
std::iterator_traits<TIterator>::iterator_category()); }

In the line marked ***, the docs suggest I should use
boost::random_access_traversal_tag instead of
std::random_access_iterator_tag - but when I do, the program no longer
selects the random access version of 'func', choosing the forward-only
version instead! The iterator_category of my_iterator ends up as:

boost::detail::iterator_category_with_traversal<std::input_iterator_tag,
boost::random_access_traversal_tag>

The reason I called this a 'deadly' bug is that std::advance is
suddenly a deathtrap:

std::advance(it, -1);

This is fine and legal for random access iterators, but if the
forward-only version gets called instead, the behavior is undefined
(on my system, it loops endlessly). The compiler doesn't complain
either way - the problem surfaces only at runtime.

Does the problem lie with iterator_category_with_traversal? It looks
like it's intended in this case to be convertible to either
std::input_iterator_tag or std::random_access_iterator_tag, but
clearly something is wrong with the latter conversion. Any ideas?

Thanks,
-Gabe


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk