4

What is the problem with this code:

#include <iostream>
#include <vector>

#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/any_range.hpp>


using namespace boost::adaptors;


using Range =
  boost::any_range<
    int,
    boost::forward_traversal_tag,
    int,
    std::ptrdiff_t>;


void magic(const Range &) {}


int main()
{
  std::vector<int> xs{0, 1, 2};

  auto ys = xs | transformed([](auto x) { return x; });

  const Range zs = ys;
  std::vector<int> us{boost::begin(zs), boost::end(zs)};
  magic(us);

  return 0;
}

Complie:

c++ -g -std=c++14 -O2 main.cpp

Run and get segfault.

But when I compile with lower optimization level, then everything is ok.

gdb output:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004011a5 in boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int, long, boost::any_iterator_buffer<64ul> >::dereference (this=<optimized out>) at /usr/include/boost/range/detail/any_iterator.hpp:512
512                     return m_impl->dereference();

Is this boost::any_range bug, or I misuse the library?

Program also crashes if I compile it this way:

c++ -g -std=c++14 -O1 -fisolate-erroneous-paths-dereference main.cpp

The program below crashes too, if I compile it with options -O1 -fisolate-erroneous-paths-dereference:

#include <iostream>
#include <vector>

#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/any_range.hpp>


using namespace boost::adaptors;


using Range =
  boost::any_range<
    int,
    boost::forward_traversal_tag,
    int &,
    std::ptrdiff_t>;


void magic(const Range &xs) {
  for (auto x: xs) { std::cout << xs; }
}


int main()
{
  std::vector<int> xs{0, 1, 2};
  auto ys = xs | transformed([](auto x) { return x; });
  magic(ys);
  return 0;
}
Paul R
  • 208,748
  • 37
  • 389
  • 560
nicolai
  • 1,140
  • 9
  • 17

2 Answers2

7

This is boost bug 10493, related to 10360, which was introduced in 1.56 (2014-08) and was only finally fixed in 1.74 (2020-08) via fix PR #94

Until 1.74, the problem is that instead of just using your reference type, it wraps it in mutable_reference_type_generator, which prevents you from being able to return a temporary. That is, when you write:

using Range =
  boost::any_range<
    int,
    boost::forward_traversal_tag,
    int,
    std::ptrdiff_t>;

You're explicitly specifying your reference type to be int, not int&, because your range is basically an input range. But the boost internals are changing it to int& anyway, so you dangle. When you write const int intsead of int, the type trait doesn't add the reference, so you actually do end up with const int.

Catskul
  • 17,916
  • 15
  • 84
  • 113
Barry
  • 286,269
  • 29
  • 621
  • 977
2

Actually when I was typing the title for my question, this question was offered as possible answer.

As suggested in the answer for the question, I replaced int with const int and it worked:

using Range =
  boost::any_range<
    int,
    boost::forward_traversal_tag,
    const int,
    std::ptrdiff_t>;

I decided to publish the question because the quoted question is not marked as answered.

Community
  • 1
  • 1
nicolai
  • 1,140
  • 9
  • 17