c++boostboost-range

How to pass boost ranges to a function that accepts any_range


I am trying to write a class function which accepts boost::any_range which is a double-random access range. My purpose is to be able to pass any kind of that range to the function which can be an std::vector, std::deque, or a boost range. The code works for std::vector and std::deque, however it gives a warning when I try with a boost range.

I tried this:

#include <boost/range/irange.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/any_range.hpp>
#include <iostream>
#include <iterator>
#include <vector>

using Range = boost::any_range<const double, boost::random_access_traversal_tag, const double>;

struct Indexer {
    Indexer(const double param_) : param(param_) {}

    double operator()(const int i) const
    {
        return static_cast<double>(i) + param;
    }

private:
    const double param;
};

class Temp {
public:
    void workWithAnyRange(const Range& range) {
        workWithAnyRangeTemplated(range);
    }

private:
    template<typename R>
    void workWithAnyRangeTemplated(const R& r) {
        for (auto i : r) {
            std::cout << "i: " << i << std::endl;
        }
    }
};

int main() {
    Temp temp;

    temp.workWithAnyRange(std::vector<double>{1,2,3});
    temp.workWithAnyRange(std::deque<double>{1,2,3});
    temp.workWithAnyRange(boost::irange(0, 10) | boost::adaptors::transformed(Indexer(15.0))); //introduces the warning

    return 0;
}

And the following output I got:

In file included from /opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator.hpp:22:0,
                 from /opt/compiler-explorer/libs/boost_1_71_0/boost/range/any_range.hpp:17,
                 from <source>:5:
/opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp: In member function 'boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::dereference() const [with WrappedIterator = boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<Indexer, double>, boost::range_detail::integer_iterator<int>, boost::use_default, boost::use_default>; Reference = double; Difference = long int; Buffer = boost::any_iterator_buffer<64ul>; boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference = double&]':
/opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp:510:57: warning: function returns address of local variable [-Wreturn-local-addr]
                 return dereference_cast<reference>(*m_it);
                                                         ^
/opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp:510:51: note: declared here
                 return dereference_cast<reference>(*m_it);
                                                   ^
ASM generation compiler returned: 0
In file included from /opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator.hpp:22:0,
                 from /opt/compiler-explorer/libs/boost_1_71_0/boost/range/any_range.hpp:17,
                 from <source>:5:
/opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp: In member function 'boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::dereference() const [with WrappedIterator = boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<Indexer, double>, boost::range_detail::integer_iterator<int>, boost::use_default, boost::use_default>; Reference = double; Difference = long int; Buffer = boost::any_iterator_buffer<64ul>; boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference = double&]':
/opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp:510:57: warning: function returns address of local variable [-Wreturn-local-addr]
                 return dereference_cast<reference>(*m_it);
                                                         ^
/opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp:510:51: note: declared here
                 return dereference_cast<reference>(*m_it);
                                                   ^
Execution build compiler returned: 0
Program returned: 0
i: 1
i: 2
i: 3
i: 1
i: 2
i: 3
i: 15
i: 16
i: 17
i: 18
i: 19
i: 20
i: 21
i: 22
i: 23
i: 24

As you see, it outputs something but the warning seems concerning. If I remove the line where I called the function with boost::irange, the warning disappears. Can anyone help me to resolve the issue?


Solution

  • I got the feeling that I messed up with the compiler options, because this seems like it works – user1508716 4 mins ago

    Nah, that's just because that includes the boost include directory with -isystem instead of -I hiding the warnings.

    The warning indicates that the result of the transform - an rvalue - is returned by reference. Rather surprising since you requested the reference type to be double const. Indeed, the error message confirms:

    /opt/compiler-explorer/libs/boost_1_71_0/boost/range/detail/any_iterator_wrapper.hpp: In member function '
    boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::dereference()
    const [with 
    WrappedIterator = boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<Indexer, double>, boost::range_detail::integer_iterator<int>, boost::use_default, boost::use_default>;
    Reference = double;
    Difference = long int;
    Buffer = boost::any_iterator_buffer<64ul>;
    boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference = double&;
    ]':
    

    Note how it comfirms that Reference = double as you requested (top-level const is not significant in function signatures in C++). However, it deems boost::range_detail::any_random_access_iterator_wrapper<WrappedIterator, Reference, Difference, Buffer>::reference = double&, making the implementation of dereference unsafe:

    virtual reference dereference() const
    {
        return dereference_cast<reference>(*m_it);
    }
    

    Comparing with Boost 1.81.0 shows no problem: https://godbolt.org/z/9qfTzn15T

    Indeed boost 1.74.0 contains a fix: https://godbolt.org/z/vEe118f6G

    Indeed boost 1.73.0 still had the problem: https://godbolt.org/z/EM56Pss5q

    The change involved appears to be

    474efda Merge pull request #94 from mjendruk/fix-any-range-non-reference-references
    

    Which links to https://github.com/boostorg/range/pull/94 "Fix any_range with non-reference references can cause UB"