Add a custom implementation of boost::adaptors::uniqued that is compatible with C++20 ranges library. This bridges the gap between Boost.Range and the C++ standard library ranges until std::views::unique becomes available in C++26. Currently, the unique view is included in [P2214](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r0.html) "A Plan for C++ Ranges Evolution", which targets C++26. The implementation provides: - A lazy view adaptor that presents unique consecutive elements - No modification of source range - Compatibility with C++20 range views and concepts - Lighter header dependencies compared to Boost This resolves compilation errors when piping C++20 range views to boost::adaptors::uniqued, which fails due to concept requirements mismatch. For example: ```c++ auto range = std::views::take(n) | boost::adaptors::uniqued; // fails ``` This change also offers us a lightweight solution in terms of smaller header dependency. While std::ranges::unique exists in C++23, it's an eager algorithm that modifies the source range in-place, unlike boost::adaptors::uniqued which is a lazy view. The proposed std::views::unique (P2214) targeting C++26 would provide this functionality, but is not yet available. This implementation serves as an interim solution for filtering consecutive duplicate elements using range views until std::views::unique is standardized. For more details on the differences between `std::ranges::unique` and `boost::adaptors::uniqued`: - boost::adaptors::uniqued is a view adaptor that creates a lazy view over the original range. It: * Doesn't modify the source range * Returns a view that presents unique consecutive elements * Is non-destructive and lazy-evaluated * Can be composed with other views - std::ranges::unique is an algorithm that: * Modifies the source range in-place * Removes consecutive duplicates by shifting elements * Returns an iterator to the new logical end * Cannot be used as a view or composed with other range adaptors Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
117 lines
3.7 KiB
C++
117 lines
3.7 KiB
C++
#define BOOST_TEST_MODULE test-ranges
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <string>
|
|
#include <ranges>
|
|
|
|
#include "utils/unique_view.hh"
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(test_empty_range) {
|
|
std::vector<int> empty;
|
|
auto view = empty | utils::views::unique;
|
|
|
|
BOOST_CHECK(std::ranges::empty(view));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_single_element) {
|
|
std::vector<int> single{42};
|
|
auto view = single | utils::views::unique;
|
|
|
|
BOOST_CHECK_EQUAL(std::ranges::distance(view), 1);
|
|
BOOST_CHECK_EQUAL(*view.begin(), 42);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_all_same_elements) {
|
|
std::vector<int> same{1, 1, 1, 1, 1};
|
|
auto view = same | utils::views::unique;
|
|
|
|
BOOST_CHECK_EQUAL(std::ranges::distance(view), 1);
|
|
BOOST_CHECK_EQUAL(*view.begin(), 1);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_all_different_elements) {
|
|
std::vector<int> different{1, 2, 3, 4, 5};
|
|
auto result = different | utils::views::unique | std::ranges::to<std::vector>();
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
different.begin(), different.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_consecutive_duplicates) {
|
|
std::vector<int> input{1, 1, 2, 2, 3, 3, 2, 2, 1, 1};
|
|
auto result = input | utils::views::unique | std::ranges::to<std::vector>();
|
|
|
|
std::vector<int> expected{1, 2, 3, 2, 1};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_string_elements) {
|
|
std::vector<std::string> input{"hello", "hello", "world", "world", "hello"};
|
|
auto result = input | utils::views::unique | std::ranges::to<std::vector>();
|
|
|
|
std::vector<std::string> expected{"hello", "world", "hello"};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_different_container_type) {
|
|
std::list<int> input{1, 1, 2, 2, 3, 3};
|
|
auto result = input | utils::views::unique | std::ranges::to<std::vector>();
|
|
|
|
std::vector<int> expected{1, 2, 3};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_const_range) {
|
|
const std::vector<int> input{1, 1, 2, 2, 3};
|
|
auto result = input | utils::views::unique | std::ranges::to<std::vector>();
|
|
|
|
std::vector<int> expected{1, 2, 3};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_non_common_range) {
|
|
auto result = std::views::iota(1)
|
|
| utils::views::unique
|
|
| std::views::take(3)
|
|
| std::ranges::to<std::vector>();
|
|
|
|
std::vector<int> expected{1, 2, 3};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_composition_with_other_views) {
|
|
std::vector<int> input{1, 1, 2, 2, 3, 3, 4, 4, 5, 5};
|
|
auto result = input
|
|
| utils::views::unique
|
|
| std::views::take(3)
|
|
| std::ranges::to<std::vector>();
|
|
|
|
std::vector<int> expected{1, 2, 3};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_function_call_syntax) {
|
|
std::vector<int> input{1, 1, 2, 2, 3};
|
|
auto result = utils::views::unique(input) | std::ranges::to<std::vector>();
|
|
|
|
std::vector<int> expected{1, 2, 3};
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
|
|
expected.begin(), expected.end());
|
|
}
|