diff --git a/src/util/range.h b/src/util/range.h index f994430b82d..72420c8404d 100644 --- a/src/util/range.h +++ b/src/util/range.h @@ -48,7 +48,7 @@ class map_iteratort PRECONDITION(underlying != underlying_end); ++underlying; if(underlying != underlying_end) - current = util_make_unique((*f)(*underlying)); + current = std::make_shared((*f)(*underlying)); return *this; } @@ -60,6 +60,16 @@ class map_iteratort return tmp; } + value_type &operator*() + { + return *current.get(); + } + + value_type *operator->() + { + return &(*current.get()); + } + const value_type &operator*() const { return *current.get(); @@ -80,16 +90,7 @@ class map_iteratort f(std::move(f)) { if(this->underlying != this->underlying_end) - current = util_make_unique((*this->f)(*this->underlying)); - } - - map_iteratort(const map_iteratort &other) - : underlying(other.underlying), - underlying_end(other.underlying_end), - f(other.f) - { - if(other.current != nullptr) - current = util_make_unique(*other.current.get()); + current = std::make_shared((*this->f)(*this->underlying)); } private: @@ -101,7 +102,7 @@ class map_iteratort /// Stores the result of \c f at the current position of the iterator. /// Equals nullptr if the iterator reached \c underlying_end. - std::unique_ptr current = nullptr; + std::shared_ptr current = nullptr; }; /// Iterator which only stops at elements for which some given function \c f is @@ -142,6 +143,16 @@ class filter_iteratort return tmp; } + value_type &operator*() + { + return *underlying; + } + + value_type *operator->() + { + return &(*underlying); + } + const value_type &operator*() const { return *underlying; @@ -234,6 +245,20 @@ struct concat_iteratort return tmp; } + value_type &operator*() + { + if(first_begin == first_end) + return *second_begin; + return *first_begin; + } + + value_type *operator->() + { + if(first_begin == first_end) + return &(*second_begin); + return &(*first_begin); + } + const value_type &operator*() const { if(first_begin == first_end) @@ -302,7 +327,11 @@ struct ranget final } /// The type of elements contained in the resulting range is deduced from the - /// return type of \p `f`. + /// return type of `f`. + /// Please note that the parameter to `f` must be a const reference. This is + /// a limitation of the current implementation. This means that you can't move + /// a value through `f`. `f` may take a move-only typed parameter by const + /// reference. 'f' may also construct and return a move-only typed value. template auto map(functiont &&f) -> ranget make_range(iteratort begin, iteratort end) return ranget(begin, end); } -template < - typename containert, - typename iteratort = typename containert::const_iterator> -ranget make_range(const containert &container) +template +auto make_range(containert &container) -> ranget { - return ranget(container.begin(), container.end()); + return ranget( + container.begin(), container.end()); } #endif // CPROVER_UTIL_RANGE_H diff --git a/unit/util/range.cpp b/unit/util/range.cpp index 7da825c42c0..156631bdfa8 100644 --- a/unit/util/range.cpp +++ b/unit/util/range.cpp @@ -65,3 +65,121 @@ SCENARIO("range tests", "[core][util][range]") } } } + +class move_onlyt +{ +public: + move_onlyt(move_onlyt &&) = default; + move_onlyt &operator=(move_onlyt &&) = default; + move_onlyt(const move_onlyt &) = delete; + move_onlyt &operator=(const move_onlyt &) = delete; + + explicit move_onlyt(int value) : value{value} {}; + int value = 0; +}; + +bool is_odd(const move_onlyt &move_only) +{ + return move_only.value % 2 != 0; +} + +const auto add = [](int left) { + return [=](const move_onlyt &right) { return left + right.value; }; +}; + +SCENARIO( + "Range tests, with collections of move only typed values.", + "[core][util][range]") +{ + GIVEN("A vector of move only typed values.") + { + std::vector input; + for(int i = 1; i <= 10; ++i) + input.emplace_back(i); + THEN("Values from a range of made from the vector can be moved.") + { + auto input_range = make_range(input); + move_onlyt destination{std::move(*input_range.begin())}; + REQUIRE(destination.value == 1); + } + THEN("A range of made from the vector can be filtered.") + { + auto odds_filter = make_range(input).filter(is_odd); + std::size_t total = 0; + for(const auto &move_only : odds_filter) + total += 1; + REQUIRE(total == 5); + auto iterator = odds_filter.begin(); + REQUIRE((iterator++)->value == 1); + REQUIRE((iterator++)->value == 3); + REQUIRE((iterator++)->value == 5); + REQUIRE((iterator++)->value == 7); + REQUIRE((iterator++)->value == 9); + } + THEN("Values from a filtered range made from the vector can be moved.") + { + std::vector odds; + for(move_onlyt &odd : make_range(input).filter(is_odd)) + odds.emplace_back(std::move(odd)); + + REQUIRE(odds.size() == 5); + REQUIRE(odds[0].value == 1); + REQUIRE(odds[1].value == 3); + REQUIRE(odds[2].value == 5); + REQUIRE(odds[3].value == 7); + REQUIRE(odds[4].value == 9); + } + THEN("Map can be applied to a range of move only typed values.") + { + std::vector results; + for(int result : make_range(input).map(add(1))) + results.push_back(result); + + const std::vector expected_results{2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + REQUIRE(results == expected_results); + } + } + GIVEN("Two vectors containing move only types values.") + { + std::vector input1; + for(int i = 1; i <= 3; ++i) + input1.emplace_back(i); + std::vector input2; + for(int i = 7; i <= 9; ++i) + input2.emplace_back(i); + + THEN("Values from concatenated ranges made from the vector can be moved.") + { + std::vector both_inputs; + for(move_onlyt &input : make_range(input1).concat(make_range(input2))) + both_inputs.emplace_back(std::move(input)); + + REQUIRE(both_inputs.size() == 6); + REQUIRE(both_inputs[0].value == 1); + REQUIRE(both_inputs[1].value == 2); + REQUIRE(both_inputs[2].value == 3); + REQUIRE(both_inputs[3].value == 7); + REQUIRE(both_inputs[4].value == 8); + REQUIRE(both_inputs[5].value == 9); + } + } + GIVEN("A const vector of ints.") + { + const std::vector input{1, 2, 3, 4, 5}; + + THEN("The vector can be mapped into a range of move-only types") + { + std::vector results; + const auto make_move_only = [](int i) { return move_onlyt{i}; }; + for(auto &incremented : make_range(input).map(make_move_only)) + results.emplace_back(std::move(incremented)); + + REQUIRE(results.size() == 5); + REQUIRE(results[0].value == 1); + REQUIRE(results[1].value == 2); + REQUIRE(results[2].value == 3); + REQUIRE(results[3].value == 4); + REQUIRE(results[4].value == 5); + } + } +}