|
1 | 1 | #ifndef CPPUTILS_FUNCTIONAL_UTILS_HPP |
2 | 2 | #define CPPUTILS_FUNCTIONAL_UTILS_HPP |
3 | 3 |
|
| 4 | +#include "../meta/traits.hpp" |
| 5 | +#include <algorithm> |
| 6 | +#include <array> |
4 | 7 | #include <concepts> |
| 8 | +#include <functional> |
| 9 | +#include <iterator> |
| 10 | +#include <ranges> |
5 | 11 | #include <utility> |
6 | 12 |
|
7 | 13 |
|
@@ -32,6 +38,63 @@ namespace cpputils { |
32 | 38 | return std::invoke(f, FWD(more_args)..., args_...); |
33 | 39 | }; |
34 | 40 | } |
| 41 | + |
| 42 | +template <std::ranges::range Range, invocable_r<std::ranges::range_value_t<Range>> Generator> |
| 43 | +struct range_maker { |
| 44 | + [[nodiscard]] constexpr auto operator()(Generator g, std::size_t num_items) const |
| 45 | + requires std::default_initializable<Range> |
| 46 | + && ( |
| 47 | + requires (Range r, Generator g_) { r.push_back(g_()); } |
| 48 | + || requires (Range r, Generator g_) { r.insert(g_()); }) |
| 49 | + { |
| 50 | + Range rng{}; |
| 51 | + if constexpr (std::ranges::sized_range<Range> && requires { rng.reserve(); }) { |
| 52 | + rng.reserve(num_items); |
| 53 | + } |
| 54 | + if constexpr (requires { rng.push_back(g()); }) { |
| 55 | + std::ranges::generate_n(std::back_inserter(rng), |
| 56 | + static_cast<std::iter_difference_t<decltype(std::back_inserter(rng))>>(num_items), |
| 57 | + g); |
| 58 | + } else { |
| 59 | + for (std::size_t i = 0; i < num_items; ++i) { |
| 60 | + rng.insert(g()); |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + return rng; |
| 65 | + } |
| 66 | +}; |
| 67 | + |
| 68 | +namespace detail { |
| 69 | + template <typename T> |
| 70 | + struct is_cstyle_array : std::false_type {}; |
| 71 | + |
| 72 | + template <typename T, std::size_t N> |
| 73 | + struct is_cstyle_array<T[N]> : std::true_type {}; // NOLINT |
| 74 | +} // namespace detail |
| 75 | + |
| 76 | +template <typename T, std::size_t N, invocable_r<T> Generator> |
| 77 | +struct range_maker<std::array<T, N>, Generator> { |
| 78 | + [[nodiscard]] constexpr auto operator()(Generator g) const { |
| 79 | + return generator_function(std::move(g), std::make_index_sequence<N>{}); |
| 80 | + } |
| 81 | + |
| 82 | +private: |
| 83 | + template <auto... Is> |
| 84 | + [[nodiscard]] static constexpr auto generator_function(Generator g, std::index_sequence<Is...>) { |
| 85 | + return std::array<T, N>{[&](auto) { return g(); }(Is)...}; |
| 86 | + } |
| 87 | +}; |
| 88 | + |
| 89 | +template <typename Range> |
| 90 | +inline constexpr auto make_range = []<typename Generator, typename... Args>(Generator g, Args... args) |
| 91 | + requires std::invocable<range_maker<Range, Generator>, Generator, Args...> |
| 92 | +{ |
| 93 | + static_assert(!detail::is_cstyle_array<Range>::value, "C-style arrays cannot be returned from functions, use std::array"); |
| 94 | + auto const factory = range_maker<Range, Generator>{}; |
| 95 | + return factory(std::move(g), args...); |
| 96 | +}; |
| 97 | + |
35 | 98 | } // namespace cpputils |
36 | 99 |
|
37 | 100 | #undef FWD |
|
0 commit comments