diff --git a/core/include/gap/core/ranges.hpp b/core/include/gap/core/ranges.hpp index 30f4946..b9957f5 100644 --- a/core/include/gap/core/ranges.hpp +++ b/core/include/gap/core/ranges.hpp @@ -43,4 +43,53 @@ namespace gap::ranges template< range R > using range_value_t = std::iter_value_t< iterator_t< R > >; + template< typename T > + concept has_reserve = requires(T t, std::size_t s) { t.reserve(s); }; + + template< typename container_t, std::ranges::input_range R > + void move_or_copy_elements(R&& rng, container_t& container) { + if constexpr (std::is_rvalue_reference_v< decltype(rng) >) { + std::move(std::ranges::begin(rng), std::ranges::end(rng), + std::back_inserter(container) + ); + } else { + std::copy(std::ranges::begin(rng), std::ranges::end(rng), + std::back_inserter(container) + ); + } + } + + template< template< typename... > class container_t > + struct to_fn { + template< std::ranges::input_range R > + auto operator()(R&& range) const { + using value_type = std::ranges::range_value_t< R >; + using result_container_t = container_t< value_type >; + + result_container_t container; + + if constexpr (has_reserve< result_container_t >) { + if constexpr (requires { std::ranges::size(range); }) { + container.reserve(std::ranges::size(range)); + } + } + + move_or_copy_elements(std::forward< R >(range), container); + return container; + } + + template< std::ranges::input_range R > + friend auto operator|(R&& range, const to_fn& to) { + return to(std::forward< R >(range)); + } + }; + + template< template< typename... > class container_t > + inline constexpr to_fn< container_t > to{}; + + template< template< typename... > class container_t, std::ranges::input_range R > + auto operator|(R&& range, to_fn< container_t > const& to) { + return to(std::forward< R >(range)); + } + } // namespace gap::ranges diff --git a/test/core/ranges.cpp b/test/core/ranges.cpp index aefff87..64d29db 100644 --- a/test/core/ranges.cpp +++ b/test/core/ranges.cpp @@ -1,9 +1,13 @@ // Copyright (c) 2022-present, Trail of Bits, Inc. -#include #include + #include + +#include #include +#include +#include namespace gap::test { @@ -31,4 +35,26 @@ namespace gap::test gr::range_value_t< std::vector< dummy > >, std::vector< dummy >::value_type >); + TEST_SUITE("ranges") { + TEST_CASE("to vector") { + std::vector< int > v = {1, 2, 3, 4, 5}; + auto v2 = v | gr::to< std::vector >; + CHECK(v == v2); + } + + TEST_CASE("to list") { + std::vector< int > v = {1, 2, 3, 4, 5}; + auto l = v | gr::to< std::list >; + + CHECK(std::equal(v.begin(), v.end(), l.begin(), l.end())); + } + + TEST_CASE("to with filter") { + std::vector< int > v = {1, 2, 3, 4, 5}; + auto v2 = v | std::views::filter([] (int x) { return x % 2 == 0; }) + | gr::to< std::vector >; + CHECK(v2 == std::vector{2, 4}); + } + } + } // namespace gap::test