4

Is it possible to use the boost::iterators library to create iterators that show the combined results of 2 ranges? Note that for usage, we need the output to be iterators. Nor do we expect to first manually create a vector of all combinations. (Note: We compile with C++17)

A contrived example:

auto v = std::vector<int>({1,2,3});
auto s = std::set<double>({0.5, 1.5});
auto range = combineElements(v.begin(), v.end(), s.begin(), s.end(), [](int i, double d){ return std::pair{i, d}; });
for (auto [i, d] : range)
   std::cout << std::to_string(i) << ',' <<std::to_string(d) << ";";
// Expected output: 1,0.5;1,1.5;2,0.5;2,1.5;3,0.5;3,1.5;

I've checked the documentation of boost::iterators and found:

  • Zip: This combined the nth element of the first range, with the nth of the second. However, doesn't combine nth with mth element.
  • Generator: The generator that needs to be provided could do manual bookkeeping keeping 6 iterators: begin, end, and current for both ranges. However, it doesn't have a way to stop it.
  • Function input: Similar to Generator, we could put those iterators in the state and create an end-state for the end-iterator using the end-iterators. However, the state ain't provided to the generator to calculate the value of the iterator.
  • Permutation: Works on a single range
  • Transform: Works on a single range

Am I correct to conclude that in this situation the only option is to write customer iterator by hand?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
JVApen
  • 11,008
  • 5
  • 31
  • 67
  • 3
    [range-v3](https://github.com/ericniebler/range-v3) has **cartesian_product** view for that. [Demo](https://godbolt.org/z/8b1YbKK14) – Jarod42 Jul 15 '21 at 09:33
  • Thanks for the tag, unfortunately, I don't have that library at my disposal. However, it might be worth looking into. – JVApen Jul 15 '21 at 10:13
  • 1
    With a quick search, I don't find cartesian_product in boost (except for hana which doesn't seem to apply here). – Jarod42 Jul 15 '21 at 10:24

1 Answers1

1

Am I correct to conclude that in this situation the only option is to write customer iterator by hand?

Pretty much yes. You might want to decompose it into an iterator adaptor that repeats each element N times, and one that repeats the whole range M times, which would allow you to zip the pair of those.

template <class Iterator>
class repeat_iterator
  : public boost::iterator::iterator_adaptor< repeat_iterator<Iterator>, Iterator > 
{
  typedef boost::iterator::iterator_adaptor< repeat_iterator<Iterator>, Iterator > super_t;
  friend class boost::iterator::iterator_core_access;
  size_t len = 1;
  size_t curr = 0;

public:
  repeat_iterator() {}

  explicit repeat_iterator(Iterator x, size_t len, curr = 0)
      : super_t(x), len(len), curr(curr) {}

private:
  void increment() { 
      if (++curr == len) { 
          curr = 0;
          ++base_reference(); 
      }
  }
  void decrement() {
      if (curr-- == 0) {
          curr = len - 1;
          --base_reference(); 
      } 
  }
  void advance(typename super_t::difference_type n)
  {
      n += curr;
      base_reference() += n / len;
      curr = n % len;
  }
  template <class OtherIterator>
  typename super_t::difference_type
  distance_to(unstride_iterator<OtherIterator> const& y) const
  {
      return ((base() - y.base()) * len) + (curr - y.curr);
  }
};

template <class Iterator>
class cycle_iterator
  : public boost::iterator::iterator_adaptor< cycle_iterator<Iterator>, Iterator > 
{
  typedef boost::iterator::iterator_adaptor< cycle_iterator<Iterator>, Iterator > super_t;
  friend class boost::iterator::iterator_core_access;
  Iterator first;
  Iterator last;

public:
  cycle_iterator() {}

  explicit cycle_iterator(Iterator first, Iterator last)
      : super_t(first), first(first), last(last) {}

private:
  void increment() { 
      if (++base_reference() == last) { 
          base_reference() = first; 
      }
  }
  void decrement() {
      if (base_reference() == first) {
          base_reference() = last - 1; 
      } 
  }
  void advance(typename super_t::difference_type n)
  {
      n += std::distance(first, base_reference());
      n %= std::distance(first, last);
      base_reference() = first + n;
  }
};
Caleth
  • 52,200
  • 2
  • 44
  • 75