From a18042bf8aab0050b1b5d67043340336f6decfe2 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 14:12:17 -0400 Subject: [PATCH 01/26] Split scan_view implementation from dependencies --- include/beman/scan_view/scan.hpp | 454 +------------------------ include/beman/scan_view/scan_view.hpp | 460 ++++++++++++++++++++++++++ 2 files changed, 461 insertions(+), 453 deletions(-) create mode 100644 include/beman/scan_view/scan_view.hpp diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index 88c6f4f..4188e90 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -11,458 +11,6 @@ #include #include -namespace beman::scan_view { - -namespace detail { - -// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not -// just move-constructible; we preserve the old behavior in pre-C++23 modes. -template -concept movable_box_object = -#if __cpp_lib_ranges >= 202207L - std::move_constructible -#else - std::copy_constructible -#endif - && std::is_object_v; - -// Primary template - uses std::optional and introduces an empty state in case assignment fails. -template -class movable_box { - [[no_unique_address]] std::optional val_; - - public: - template - requires std::is_constructible_v - constexpr explicit movable_box(std::in_place_t, - Args&&... args) noexcept(std::is_nothrow_constructible_v) - : val_(std::in_place, std::forward(args)...) {} - - constexpr movable_box() noexcept(std::is_nothrow_default_constructible_v) - requires std::default_initializable - : val_(std::in_place) {} - - movable_box(const movable_box&) = default; - movable_box(movable_box&&) = default; - - constexpr movable_box& operator=(const movable_box& other) noexcept(std::is_nothrow_copy_constructible_v) -#if __cpp_lib_ranges >= 202207L - requires std::copy_constructible -#endif - { - if (this != std::addressof(other)) { - if (other.has_value()) - val_.emplace(*other); - else - val_.reset(); - } - return *this; - } - - movable_box& operator=(movable_box&&) - requires std::movable - = default; - - constexpr movable_box& operator=(movable_box&& other) noexcept(std::is_nothrow_move_constructible_v) { - if (this != std::addressof(other)) { - if (other.has_value()) - val_.emplace(std::move(*other)); - else - val_.reset(); - } - return *this; - } - - constexpr const Tp& operator*() const noexcept { return *val_; } - constexpr Tp& operator*() noexcept { return *val_; } - - constexpr const Tp* operator->() const noexcept { return val_.operator->(); } - constexpr Tp* operator->() noexcept { return val_.operator->(); } - - [[nodiscard]] constexpr bool has_value() const noexcept { return val_.has_value(); } -}; - -template -using maybe_const = std::conditional_t; - -template -struct perfect_forward_impl; - -template -struct perfect_forward_impl, BoundArgs...> { - private: - std::tuple bound_args_; - - public: - template , Args&&...>>> - explicit constexpr perfect_forward_impl(Args&&... bound_args) : bound_args_(std::forward(bound_args)...) {} - - perfect_forward_impl(const perfect_forward_impl&) = default; - perfect_forward_impl(perfect_forward_impl&&) = default; - - perfect_forward_impl& operator=(const perfect_forward_impl&) = default; - perfect_forward_impl& operator=(perfect_forward_impl&&) = default; - - template >> - constexpr auto - operator()(Args&&... args) & noexcept(noexcept(Op()(std::get(bound_args_)..., std::forward(args)...))) - -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { - return Op()(std::get(bound_args_)..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) & = delete; - - template >> - constexpr auto operator()(Args&&... args) const& noexcept(noexcept(Op()(std::get(bound_args_)..., - std::forward(args)...))) - -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { - return Op()(std::get(bound_args_)..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) const& = delete; - - template >> - constexpr auto operator()(Args&&... args) && noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., - std::forward(args)...))) - -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { - return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) && = delete; - - template >> - constexpr auto operator()(Args&&... args) const&& noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., - std::forward(args)...))) - -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { - return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) const&& = delete; -}; - -// perfect_forward implements a perfect-forwarding call wrapper as explained in [func.require]. -template -using perfect_forward = perfect_forward_impl, Args...>; - -struct compose_op { - template - constexpr auto operator()(Fn1&& f1, Fn2&& f2, Args&&... args) const - noexcept(noexcept(std::invoke(std::forward(f1), - std::invoke(std::forward(f2), std::forward(args)...)))) - -> decltype(std::invoke(std::forward(f1), - std::invoke(std::forward(f2), std::forward(args)...))) { - return std::invoke(std::forward(f1), std::invoke(std::forward(f2), std::forward(args)...)); - } -}; - -template -struct compose_t : perfect_forward { - using perfect_forward::perfect_forward; -}; - -template -constexpr auto compose(Fn1&& f1, Fn2&& f2) noexcept( - noexcept(compose_t, std::decay_t>(std::forward(f1), std::forward(f2)))) - -> decltype(compose_t, std::decay_t>(std::forward(f1), std::forward(f2))) { - return compose_t, std::decay_t>(std::forward(f1), std::forward(f2)); -} - -// CRTP base that one can derive from in order to be considered a range adaptor closure -// by the library. When deriving from this class, a pipe operator will be provided to -// make the following hold: -// - `x | f` is equivalent to `f(x)` -// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))` -template -struct range_adaptor_closure; - -// Type that wraps an arbitrary function object and makes it into a range adaptor closure, -// i.e. something that can be called via the `x | f` notation. -template -struct range_adaptor_closure_t : Fn, range_adaptor_closure> { - constexpr explicit range_adaptor_closure_t(Fn&& f) : Fn(std::move(f)) {} -}; - -template -concept RangeAdaptorClosure = - std::derived_from, range_adaptor_closure>>; - -template -struct range_adaptor_closure { - template - requires std::same_as> && std::invocable - [[nodiscard]] friend constexpr decltype(auto) - operator|(View&& view, Closure&& closure) noexcept(std::is_nothrow_invocable_v) { - return std::invoke(std::forward(closure), std::forward(view)); - } - - template - requires std::same_as> && - std::constructible_from, Closure> && - std::constructible_from, OtherClosure> - [[nodiscard]] friend constexpr auto - operator|(Closure&& c1, - OtherClosure&& c2) noexcept(std::is_nothrow_constructible_v, Closure> && - std::is_nothrow_constructible_v, OtherClosure>) { - return range_adaptor_closure_t(compose(std::forward(c2), std::forward(c1))); - } -}; - -template > -struct bind_back_op; - -template -struct bind_back_op> { - template - constexpr auto operator()(Fn&& f, BoundArgs&& bound_args, Args&&... args) const noexcept(noexcept(std::invoke( - std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...))) - -> decltype(std::invoke(std::forward(f), - std::forward(args)..., - std::get(std::forward(bound_args))...)) { - return std::invoke( - std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...); - } -}; - -template -struct bind_back_t : perfect_forward>, Fn, BoundArgs> { - using perfect_forward>, Fn, BoundArgs>::perfect_forward; -}; - -template - requires std::is_constructible_v, Fn> && std::is_move_constructible_v> && - (std::is_constructible_v, Args> && ...) && - (std::is_move_constructible_v> && ...) -constexpr auto -bind_back(Fn&& f, Args&&... args) noexcept(noexcept(bind_back_t, std::tuple...>>( - std::forward(f), std::forward_as_tuple(std::forward(args)...)))) - -> decltype(bind_back_t, std::tuple...>>( - std::forward(f), std::forward_as_tuple(std::forward(args)...))) { - return bind_back_t, std::tuple...>>( - std::forward(f), std::forward_as_tuple(std::forward(args)...)); -} - -template -constexpr bool tidy_func = - std::is_empty_v && std::is_trivially_default_constructible_v && std::is_trivially_destructible_v; - -} // namespace detail - -enum class scan_view_kind : bool { unseeded, seeded }; - -template -concept scannable_impl = // exposition only - std::movable && std::convertible_to && std::invocable> && - std::assignable_from>>; - -template -concept scannable = // exposition only - std::invocable> && - std::convertible_to>, - std::decay_t>>> && - scannable_impl>>>; - -template - requires std::ranges::view && std::is_object_v && std::is_object_v && scannable -class scan_view : public std::ranges::view_interface> { - private: - // [range.scan.iterator], class template scan_view::iterator - template - class iterator; // exposition only - - template - friend class iterator; // exposition only - - V base_ = V(); // exposition only - detail::movable_box fun_; // exposition only - detail::movable_box init_; // exposition only - - public: - scan_view() - requires std::default_initializable && std::default_initializable - = default; - constexpr explicit scan_view(V base, F fun) - requires(K == scan_view_kind::unseeded) - : base_{std::move(base)}, fun_{std::in_place, std::move(fun)} {} - constexpr explicit scan_view(V base, F fun, T init) - requires(K == scan_view_kind::seeded) - : base_{std::move(base)}, fun_{std::in_place, std::move(fun)}, init_{std::in_place, std::move(init)} {} - - constexpr V base() const& - requires std::copy_constructible - { - return base_; - } - constexpr V base() && { return std::move(base_); } - - constexpr iterator begin() { return iterator{*this, std::ranges::begin(base_)}; } - constexpr iterator begin() const - requires std::ranges::range && scannable - { - return iterator{*this, std::ranges::begin(base_)}; - } - - [[nodiscard]] constexpr std::default_sentinel_t end() const noexcept { return std::default_sentinel; } - - constexpr auto size() - requires std::ranges::sized_range - { - return std::ranges::size(base_); - } - constexpr auto size() const - requires std::ranges::sized_range - { - return std::ranges::size(base_); - } - -#if __cpp_lib_ranges_reserve_hint >= 202502L - constexpr auto reserve_hint() - requires std::ranges::approximately_sized_range - { - return std::ranges::reserve_hint(base_); - } - - constexpr auto reserve_hint() const - requires std::ranges::approximately_sized_range - { - return std::ranges::reserve_hint(base_); - } -#endif -}; - -template -scan_view(R&&, F) -> scan_view, F, std::ranges::range_value_t>; -template -scan_view(R&&, F, T) -> scan_view, F, T, scan_view_kind::seeded>; - -template - requires std::ranges::view && std::is_object_v && std::is_object_v && scannable -template -class scan_view::iterator { - private: - using Parent = detail::maybe_const; // exposition only - using Base = detail::maybe_const; // exposition only - using Func = detail::maybe_const; // exposition only - using ResultType = std::decay_t< // exposition only - std::invoke_result_t>>; - - struct Holder { // exposition only - std::ranges::sentinel_t end_ = std::ranges::sentinel_t(); // exposition only - detail::movable_box fun_; // exposition only - detail::movable_box init_; // exposition only - }; - using HolderType = std::conditional_t, Holder, Parent*>; // exposition only - - std::ranges::iterator_t current_ = std::ranges::iterator_t(); // exposition only - HolderType parent_ = {}; // exposition only - detail::movable_box sum_; // exposition only - - constexpr std::ranges::sentinel_t get_end() const { // exposition only - if constexpr (detail::tidy_func) - return parent_.end_; - else - return std::ranges::end(parent_->base_); - } - constexpr Func& get_fun() { // exposition only - if constexpr (detail::tidy_func) - return *parent_.fun_; - else - return *parent_->fun_; - } - constexpr T& get_init() { // exposition only - if constexpr (detail::tidy_func) - return *parent_.init_; - else - return *parent_->init_; - } - static constexpr HolderType init(Parent& parent) { // exposition only - if constexpr (detail::tidy_func) - return {std::ranges::end(parent.base_), detail::movable_box{std::in_place}, parent.init_}; - else - return std::addressof(parent); - } - - public: - using iterator_concept = std::input_iterator_tag; - using value_type = ResultType; - using difference_type = std::ranges::range_difference_t; - - iterator() - requires std::default_initializable> - = default; - constexpr iterator(Parent& parent, std::ranges::iterator_t current) - : current_{std::move(current)}, parent_{init(parent)} { - if (current_ == get_end()) - return; - if constexpr (K == scan_view_kind::seeded) { - sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), get_init(), *current_)}; - } else { - sum_ = detail::movable_box{std::in_place, *current_}; - } - } - constexpr iterator(iterator i) - requires Const && std::convertible_to, std::ranges::iterator_t> - : current_{std::move(i.current_)}, parent_{std::move(i.parent_)}, sum_{std::move(i.sum_)} {} - - constexpr const std::ranges::iterator_t& base() const& noexcept { return current_; } - constexpr std::ranges::iterator_t base() && { return std::move(current_); } - - constexpr const value_type& operator*() const { return *sum_; } - - constexpr iterator& operator++() { - if (++current_ != get_end()) { - sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), std::move(*sum_), *current_)}; - } - return *this; - } - constexpr void operator++(int) { ++*this; } - - friend constexpr bool operator==(const iterator& x, const iterator& y) - requires std::equality_comparable> - { - return x.current_ == y.current_; - } - friend constexpr bool operator==(const iterator& x, std::default_sentinel_t) { return x.current_ == x.get_end(); } -}; - -namespace detail { - -struct scan_t { - constexpr scan_t() = default; - constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F) const { - return scan_view{std::forward(E), std::forward(F)}; - } - constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F, auto&& G) const { - return scan_view{std::forward(E), std::forward(F), std::forward(G)}; - } - - constexpr auto operator()(auto&& E) const { - return detail::range_adaptor_closure_t(detail::bind_back(*this, std::forward(E))); - } - constexpr auto operator()(auto&& E, auto&& F) const { - return detail::range_adaptor_closure_t( - detail::bind_back(*this, std::forward(E), std::forward(F))); - } -}; - -} // namespace detail - -inline constexpr detail::scan_t scan{}; - -} // namespace beman::scan_view - -// Conditionally borrowed range (P3117) -template -constexpr bool std::ranges::enable_borrowed_range> = - std::ranges::enable_borrowed_range && beman::scan_view::detail::tidy_func; +#include #endif // BEMAN_SCAN_VIEW_SCAN_HPP diff --git a/include/beman/scan_view/scan_view.hpp b/include/beman/scan_view/scan_view.hpp new file mode 100644 index 0000000..9502bfb --- /dev/null +++ b/include/beman/scan_view/scan_view.hpp @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_SCAN_VIEW_SCAN_VIEW_HPP +#define BEMAN_SCAN_VIEW_SCAN_VIEW_HPP + +namespace beman::scan_view { + +namespace detail { + +// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not +// just move-constructible; we preserve the old behavior in pre-C++23 modes. +template +concept movable_box_object = +#if __cpp_lib_ranges >= 202207L + std::move_constructible +#else + std::copy_constructible +#endif + && std::is_object_v; + +// Primary template - uses std::optional and introduces an empty state in case assignment fails. +template +class movable_box { + [[no_unique_address]] std::optional val_; + + public: + template + requires std::is_constructible_v + constexpr explicit movable_box(std::in_place_t, + Args&&... args) noexcept(std::is_nothrow_constructible_v) + : val_(std::in_place, std::forward(args)...) {} + + constexpr movable_box() noexcept(std::is_nothrow_default_constructible_v) + requires std::default_initializable + : val_(std::in_place) {} + + movable_box(const movable_box&) = default; + movable_box(movable_box&&) = default; + + constexpr movable_box& operator=(const movable_box& other) noexcept(std::is_nothrow_copy_constructible_v) +#if __cpp_lib_ranges >= 202207L + requires std::copy_constructible +#endif + { + if (this != std::addressof(other)) { + if (other.has_value()) + val_.emplace(*other); + else + val_.reset(); + } + return *this; + } + + movable_box& operator=(movable_box&&) + requires std::movable + = default; + + constexpr movable_box& operator=(movable_box&& other) noexcept(std::is_nothrow_move_constructible_v) { + if (this != std::addressof(other)) { + if (other.has_value()) + val_.emplace(std::move(*other)); + else + val_.reset(); + } + return *this; + } + + constexpr const Tp& operator*() const noexcept { return *val_; } + constexpr Tp& operator*() noexcept { return *val_; } + + constexpr const Tp* operator->() const noexcept { return val_.operator->(); } + constexpr Tp* operator->() noexcept { return val_.operator->(); } + + [[nodiscard]] constexpr bool has_value() const noexcept { return val_.has_value(); } +}; + +template +using maybe_const = std::conditional_t; + +template +struct perfect_forward_impl; + +template +struct perfect_forward_impl, BoundArgs...> { + private: + std::tuple bound_args_; + + public: + template , Args&&...>>> + explicit constexpr perfect_forward_impl(Args&&... bound_args) : bound_args_(std::forward(bound_args)...) {} + + perfect_forward_impl(const perfect_forward_impl&) = default; + perfect_forward_impl(perfect_forward_impl&&) = default; + + perfect_forward_impl& operator=(const perfect_forward_impl&) = default; + perfect_forward_impl& operator=(perfect_forward_impl&&) = default; + + template >> + constexpr auto + operator()(Args&&... args) & noexcept(noexcept(Op()(std::get(bound_args_)..., std::forward(args)...))) + -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { + return Op()(std::get(bound_args_)..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) & = delete; + + template >> + constexpr auto operator()(Args&&... args) const& noexcept(noexcept(Op()(std::get(bound_args_)..., + std::forward(args)...))) + -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { + return Op()(std::get(bound_args_)..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) const& = delete; + + template >> + constexpr auto operator()(Args&&... args) && noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., + std::forward(args)...))) + -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { + return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) && = delete; + + template >> + constexpr auto operator()(Args&&... args) const&& noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., + std::forward(args)...))) + -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { + return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) const&& = delete; +}; + +// perfect_forward implements a perfect-forwarding call wrapper as explained in [func.require]. +template +using perfect_forward = perfect_forward_impl, Args...>; + +struct compose_op { + template + constexpr auto operator()(Fn1&& f1, Fn2&& f2, Args&&... args) const + noexcept(noexcept(std::invoke(std::forward(f1), + std::invoke(std::forward(f2), std::forward(args)...)))) + -> decltype(std::invoke(std::forward(f1), + std::invoke(std::forward(f2), std::forward(args)...))) { + return std::invoke(std::forward(f1), std::invoke(std::forward(f2), std::forward(args)...)); + } +}; + +template +struct compose_t : perfect_forward { + using perfect_forward::perfect_forward; +}; + +template +constexpr auto compose(Fn1&& f1, Fn2&& f2) noexcept( + noexcept(compose_t, std::decay_t>(std::forward(f1), std::forward(f2)))) + -> decltype(compose_t, std::decay_t>(std::forward(f1), std::forward(f2))) { + return compose_t, std::decay_t>(std::forward(f1), std::forward(f2)); +} + +// CRTP base that one can derive from in order to be considered a range adaptor closure +// by the library. When deriving from this class, a pipe operator will be provided to +// make the following hold: +// - `x | f` is equivalent to `f(x)` +// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))` +template +struct range_adaptor_closure; + +// Type that wraps an arbitrary function object and makes it into a range adaptor closure, +// i.e. something that can be called via the `x | f` notation. +template +struct range_adaptor_closure_t : Fn, range_adaptor_closure> { + constexpr explicit range_adaptor_closure_t(Fn&& f) : Fn(std::move(f)) {} +}; + +template +concept RangeAdaptorClosure = + std::derived_from, range_adaptor_closure>>; + +template +struct range_adaptor_closure { + template + requires std::same_as> && std::invocable + [[nodiscard]] friend constexpr decltype(auto) + operator|(View&& view, Closure&& closure) noexcept(std::is_nothrow_invocable_v) { + return std::invoke(std::forward(closure), std::forward(view)); + } + + template + requires std::same_as> && + std::constructible_from, Closure> && + std::constructible_from, OtherClosure> + [[nodiscard]] friend constexpr auto + operator|(Closure&& c1, + OtherClosure&& c2) noexcept(std::is_nothrow_constructible_v, Closure> && + std::is_nothrow_constructible_v, OtherClosure>) { + return range_adaptor_closure_t(compose(std::forward(c2), std::forward(c1))); + } +}; + +template > +struct bind_back_op; + +template +struct bind_back_op> { + template + constexpr auto operator()(Fn&& f, BoundArgs&& bound_args, Args&&... args) const noexcept(noexcept(std::invoke( + std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...))) + -> decltype(std::invoke(std::forward(f), + std::forward(args)..., + std::get(std::forward(bound_args))...)) { + return std::invoke( + std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...); + } +}; + +template +struct bind_back_t : perfect_forward>, Fn, BoundArgs> { + using perfect_forward>, Fn, BoundArgs>::perfect_forward; +}; + +template + requires std::is_constructible_v, Fn> && std::is_move_constructible_v> && + (std::is_constructible_v, Args> && ...) && + (std::is_move_constructible_v> && ...) +constexpr auto +bind_back(Fn&& f, Args&&... args) noexcept(noexcept(bind_back_t, std::tuple...>>( + std::forward(f), std::forward_as_tuple(std::forward(args)...)))) + -> decltype(bind_back_t, std::tuple...>>( + std::forward(f), std::forward_as_tuple(std::forward(args)...))) { + return bind_back_t, std::tuple...>>( + std::forward(f), std::forward_as_tuple(std::forward(args)...)); +} + +template +constexpr bool tidy_func = + std::is_empty_v && std::is_trivially_default_constructible_v && std::is_trivially_destructible_v; + +} // namespace detail + +enum class scan_view_kind : bool { unseeded, seeded }; + +template +concept scannable_impl = // exposition only + std::movable && std::convertible_to && std::invocable> && + std::assignable_from>>; + +template +concept scannable = // exposition only + std::invocable> && + std::convertible_to>, + std::decay_t>>> && + scannable_impl>>>; + +template + requires std::ranges::view && std::is_object_v && std::is_object_v && scannable +class scan_view : public std::ranges::view_interface> { + private: + // [range.scan.iterator], class template scan_view::iterator + template + class iterator; // exposition only + + template + friend class iterator; // exposition only + + V base_ = V(); // exposition only + detail::movable_box fun_; // exposition only + detail::movable_box init_; // exposition only + + public: + scan_view() + requires std::default_initializable && std::default_initializable + = default; + constexpr explicit scan_view(V base, F fun) + requires(K == scan_view_kind::unseeded) + : base_{std::move(base)}, fun_{std::in_place, std::move(fun)} {} + constexpr explicit scan_view(V base, F fun, T init) + requires(K == scan_view_kind::seeded) + : base_{std::move(base)}, fun_{std::in_place, std::move(fun)}, init_{std::in_place, std::move(init)} {} + + constexpr V base() const& + requires std::copy_constructible + { + return base_; + } + constexpr V base() && { return std::move(base_); } + + constexpr iterator begin() { return iterator{*this, std::ranges::begin(base_)}; } + constexpr iterator begin() const + requires std::ranges::range && scannable + { + return iterator{*this, std::ranges::begin(base_)}; + } + + [[nodiscard]] constexpr std::default_sentinel_t end() const noexcept { return std::default_sentinel; } + + constexpr auto size() + requires std::ranges::sized_range + { + return std::ranges::size(base_); + } + constexpr auto size() const + requires std::ranges::sized_range + { + return std::ranges::size(base_); + } + +#if __cpp_lib_ranges_reserve_hint >= 202502L + constexpr auto reserve_hint() + requires std::ranges::approximately_sized_range + { + return std::ranges::reserve_hint(base_); + } + + constexpr auto reserve_hint() const + requires std::ranges::approximately_sized_range + { + return std::ranges::reserve_hint(base_); + } +#endif +}; + +template +scan_view(R&&, F) -> scan_view, F, std::ranges::range_value_t>; +template +scan_view(R&&, F, T) -> scan_view, F, T, scan_view_kind::seeded>; + +template + requires std::ranges::view && std::is_object_v && std::is_object_v && scannable +template +class scan_view::iterator { + private: + using Parent = detail::maybe_const; // exposition only + using Base = detail::maybe_const; // exposition only + using Func = detail::maybe_const; // exposition only + using ResultType = std::decay_t< // exposition only + std::invoke_result_t>>; + + struct Holder { // exposition only + std::ranges::sentinel_t end_ = std::ranges::sentinel_t(); // exposition only + detail::movable_box fun_; // exposition only + detail::movable_box init_; // exposition only + }; + using HolderType = std::conditional_t, Holder, Parent*>; // exposition only + + std::ranges::iterator_t current_ = std::ranges::iterator_t(); // exposition only + HolderType parent_ = {}; // exposition only + detail::movable_box sum_; // exposition only + + constexpr std::ranges::sentinel_t get_end() const { // exposition only + if constexpr (detail::tidy_func) + return parent_.end_; + else + return std::ranges::end(parent_->base_); + } + constexpr Func& get_fun() { // exposition only + if constexpr (detail::tidy_func) + return *parent_.fun_; + else + return *parent_->fun_; + } + constexpr T& get_init() { // exposition only + if constexpr (detail::tidy_func) + return *parent_.init_; + else + return *parent_->init_; + } + static constexpr HolderType init(Parent& parent) { // exposition only + if constexpr (detail::tidy_func) + return {std::ranges::end(parent.base_), detail::movable_box{std::in_place}, parent.init_}; + else + return std::addressof(parent); + } + + public: + using iterator_concept = std::input_iterator_tag; + using value_type = ResultType; + using difference_type = std::ranges::range_difference_t; + + iterator() + requires std::default_initializable> + = default; + constexpr iterator(Parent& parent, std::ranges::iterator_t current) + : current_{std::move(current)}, parent_{init(parent)} { + if (current_ == get_end()) + return; + if constexpr (K == scan_view_kind::seeded) { + sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), get_init(), *current_)}; + } else { + sum_ = detail::movable_box{std::in_place, *current_}; + } + } + constexpr iterator(iterator i) + requires Const && std::convertible_to, std::ranges::iterator_t> + : current_{std::move(i.current_)}, parent_{std::move(i.parent_)}, sum_{std::move(i.sum_)} {} + + constexpr const std::ranges::iterator_t& base() const& noexcept { return current_; } + constexpr std::ranges::iterator_t base() && { return std::move(current_); } + + constexpr const value_type& operator*() const { return *sum_; } + + constexpr iterator& operator++() { + if (++current_ != get_end()) { + sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), std::move(*sum_), *current_)}; + } + return *this; + } + constexpr void operator++(int) { ++*this; } + + friend constexpr bool operator==(const iterator& x, const iterator& y) + requires std::equality_comparable> + { + return x.current_ == y.current_; + } + friend constexpr bool operator==(const iterator& x, std::default_sentinel_t) { return x.current_ == x.get_end(); } +}; + +namespace detail { + +struct scan_t { + constexpr scan_t() = default; + constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F) const { + return scan_view{std::forward(E), std::forward(F)}; + } + constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F, auto&& G) const { + return scan_view{std::forward(E), std::forward(F), std::forward(G)}; + } + + constexpr auto operator()(auto&& E) const { + return detail::range_adaptor_closure_t(detail::bind_back(*this, std::forward(E))); + } + constexpr auto operator()(auto&& E, auto&& F) const { + return detail::range_adaptor_closure_t( + detail::bind_back(*this, std::forward(E), std::forward(F))); + } +}; + +} // namespace detail + +inline constexpr detail::scan_t scan{}; + +} // namespace beman::scan_view + +// Conditionally borrowed range (P3117) +template +constexpr bool std::ranges::enable_borrowed_range> = + std::ranges::enable_borrowed_range && beman::scan_view::detail::tidy_func; + +#endif // BEMAN_SCAN_VIEW_SCAN_VIEW_HPP From e1d5324840fd1612359dccb85857088d86fe098e Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 14:16:31 -0400 Subject: [PATCH 02/26] Copied enable-experimental-import-std.cmake from exemplar --- CMakeLists.txt | 2 + .../enable-experimental-import-std.cmake | 194 ++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 infra/cmake/enable-experimental-import-std.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 40318cc..2ea8039 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.30...4.3) +include(infra/cmake/enable-experimental-import-std.cmake) + project( beman.scan_view DESCRIPTION diff --git a/infra/cmake/enable-experimental-import-std.cmake b/infra/cmake/enable-experimental-import-std.cmake new file mode 100644 index 0000000..a45c699 --- /dev/null +++ b/infra/cmake/enable-experimental-import-std.cmake @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +if(CMAKE_VERSION VERSION_EQUAL "3.30.0") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.1") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.3") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.4") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.5") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.6") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.7") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.8") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.30.9") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.0") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.1") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.10") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.11") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.12") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.3") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.4") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.5") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.6") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.7") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.8") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "3.31.9") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.0") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "a9e1cf81-9932-4810-974b-6eccaf14e457" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.1") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "a9e1cf81-9932-4810-974b-6eccaf14e457" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "a9e1cf81-9932-4810-974b-6eccaf14e457" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.3") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.4") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.5") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.6") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.0.7") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.0") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.1") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.3") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.4") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.5") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.1.6") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.2.0") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.2.1") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.2.2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.2.3") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.2.4") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.2.5") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "d0edc3af-4c50-42ea-a356-e2862fe7a444" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.3.0") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "451f2fe2-a8a2-47c3-bc32-94786d8fc91b" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.3.1") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "451f2fe2-a8a2-47c3-bc32-94786d8fc91b" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.3.2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "451f2fe2-a8a2-47c3-bc32-94786d8fc91b" + ) +elseif(CMAKE_VERSION VERSION_EQUAL "4.3.20260507-g46410ea") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "451f2fe2-a8a2-47c3-bc32-94786d8fc91b" + ) +endif() From a4c5ac4949c6e46ccc9a3bc5454dc5896c1cb7a8 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 14:45:37 -0400 Subject: [PATCH 03/26] Implement modules support --- CMakeLists.txt | 53 +++++++++++++++---- include/beman/scan_view/CMakeLists.txt | 25 ++++++++- include/beman/scan_view/config.hpp | 12 +++++ .../beman/scan_view/config_generated.hpp.in | 8 +++ include/beman/scan_view/scan_view.cppm | 19 +++++++ tests/beman/scan_view/CMakeLists.txt | 7 +++ tests/beman/scan_view/basic.test.cpp | 12 +++++ 7 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 include/beman/scan_view/config.hpp create mode 100644 include/beman/scan_view/config_generated.hpp.in create mode 100644 include/beman/scan_view/scan_view.cppm diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ea8039..6f35273 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,22 +26,55 @@ option( ${PROJECT_IS_TOP_LEVEL} ) +option(BEMAN_SCAN_VIEW_USE_MODULES "Provide beman.scan_view as a C++ module" OFF) + +if(BEMAN_SCAN_VIEW_USE_MODULES) + set(CMAKE_CXX_SCAN_FOR_MODULES ON) +endif() + +configure_file( + "${PROJECT_SOURCE_DIR}/include/beman/scan_view/config_generated.hpp.in" + "${PROJECT_BINARY_DIR}/include/beman/scan_view/config_generated.hpp" + @ONLY +) + # for find of beman_install_library and configure_build_telemetry include(infra/cmake/beman-install-library.cmake) include(infra/cmake/BuildTelemetryConfig.cmake) -add_library(beman.scan_view INTERFACE) +if(BEMAN_SCAN_VIEW_USE_MODULES) + add_library(beman.scan_view STATIC) +else() + add_library(beman.scan_view INTERFACE) +endif() add_library(beman::scan_view ALIAS beman.scan_view) -target_sources( - beman.scan_view - PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" -) - -set_target_properties( - beman.scan_view - PROPERTIES VERIFY_INTERFACE_HEADER_SETS ${PROJECT_IS_TOP_LEVEL} -) +if(BEMAN_SCAN_VIEW_USE_MODULES) + target_sources( + beman.scan_view + PUBLIC + FILE_SET CXX_MODULES + FILE_SET HEADERS + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_CURRENT_BINARY_DIR}/include" + ) + set_target_properties(beman.scan_view PROPERTIES CXX_MODULE_STD ON) + target_compile_features(beman.scan_view PUBLIC cxx_std_23) +else() + target_sources( + beman.scan_view + PUBLIC + FILE_SET HEADERS + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_CURRENT_BINARY_DIR}/include" + ) + set_target_properties( + beman.scan_view + PROPERTIES VERIFY_INTERFACE_HEADER_SETS ${PROJECT_IS_TOP_LEVEL} + ) +endif() add_subdirectory(include/beman/scan_view) diff --git a/include/beman/scan_view/CMakeLists.txt b/include/beman/scan_view/CMakeLists.txt index f814c2b..115d255 100644 --- a/include/beman/scan_view/CMakeLists.txt +++ b/include/beman/scan_view/CMakeLists.txt @@ -1,3 +1,26 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -target_sources(beman.scan_view PUBLIC FILE_SET HEADERS FILES scan.hpp) +if(BEMAN_SCAN_VIEW_USE_MODULES) + target_sources( + beman.scan_view + PUBLIC + FILE_SET CXX_MODULES FILES scan_view.cppm + FILE_SET HEADERS + FILES + config.hpp + scan_view.hpp + scan.hpp + "${PROJECT_BINARY_DIR}/include/beman/scan_view/config_generated.hpp" + ) +else() + target_sources( + beman.scan_view + PUBLIC + FILE_SET HEADERS + FILES + config.hpp + scan_view.hpp + scan.hpp + "${PROJECT_BINARY_DIR}/include/beman/scan_view/config_generated.hpp" + ) +endif() \ No newline at end of file diff --git a/include/beman/scan_view/config.hpp b/include/beman/scan_view/config.hpp new file mode 100644 index 0000000..e89f3f6 --- /dev/null +++ b/include/beman/scan_view/config.hpp @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_SCAN_VIEW_CONFIG_HPP +#define BEMAN_SCAN_VIEW_CONFIG_HPP + +#if !defined(__has_include) || __has_include() + #include +#else + #define BEMAN_SCAN_VIEW_USE_MODULES() 0 +#endif + +#endif diff --git a/include/beman/scan_view/config_generated.hpp.in b/include/beman/scan_view/config_generated.hpp.in new file mode 100644 index 0000000..d989458 --- /dev/null +++ b/include/beman/scan_view/config_generated.hpp.in @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_SCAN_VIEW_CONFIG_GENERATED_HPP +#define BEMAN_SCAN_VIEW_CONFIG_GENERATED_HPP + +#cmakedefine01 BEMAN_SCAN_VIEW_USE_MODULES() + +#endif diff --git a/include/beman/scan_view/scan_view.cppm b/include/beman/scan_view/scan_view.cppm new file mode 100644 index 0000000..17eb087 --- /dev/null +++ b/include/beman/scan_view/scan_view.cppm @@ -0,0 +1,19 @@ +module; + +#include +#include +#include +#include +#include +#include +#include + +export module beman.scan_view; + +#define BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT +export { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winclude-angled-in-module-purview" +#include +#pragma clang diagnostic pop +} diff --git a/tests/beman/scan_view/CMakeLists.txt b/tests/beman/scan_view/CMakeLists.txt index 95f0145..10af86b 100644 --- a/tests/beman/scan_view/CMakeLists.txt +++ b/tests/beman/scan_view/CMakeLists.txt @@ -9,5 +9,12 @@ target_link_libraries( PRIVATE beman::scan_view GTest::gtest GTest::gtest_main ) +if(BEMAN_SCAN_VIEW_USE_MODULES) + set_target_properties( + beman.scan_view.tests.basic + PROPERTIES CXX_MODULE_STD ON + ) +endif() + include(GoogleTest) gtest_discover_tests(beman.scan_view.tests.basic DISCOVERY_TIMEOUT 60) diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index 262440d..61a1a5a 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -1,5 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include + +#if BEMAN_SCAN_VIEW_USE_MODULES() + +#include + +import beman.scan_view; + +#else + #include #include #include @@ -9,6 +19,8 @@ #include +#endif + namespace exe = beman::scan_view; template > From a8cd17753174f86948f27fbc42d0c652e28fcd01 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 15:02:34 -0400 Subject: [PATCH 04/26] Implement modules support in examples --- examples/CMakeLists.txt | 6 ++++++ examples/basic_example.cpp | 11 +++++++++++ examples/complex_example.cpp | 11 +++++++++++ 3 files changed, 28 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a6a7e67..274e508 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,4 +10,10 @@ foreach(example ${ALL_EXAMPLES}) beman.scan_view.examples.${example} PRIVATE beman::scan_view ) + if(BEMAN_SCAN_VIEW_USE_MODULES) + set_target_properties( + beman.scan_view.examples.${example} + PROPERTIES CXX_MODULE_STD ON + ) + endif() endforeach() diff --git a/examples/basic_example.cpp b/examples/basic_example.cpp index eee75de..a40c0ce 100644 --- a/examples/basic_example.cpp +++ b/examples/basic_example.cpp @@ -1,3 +1,12 @@ +#include + +#if BEMAN_SCAN_VIEW_USE_MODULES() + +import std; +import beman.scan_view; + +#else + #include #include #include @@ -8,6 +17,8 @@ #include +#endif + namespace exe = beman::scan_view; #if __cpp_lib_print >= 202207L && __cpp_lib_format_ranges >= 202207L diff --git a/examples/complex_example.cpp b/examples/complex_example.cpp index d9a0f5e..a78c9d8 100644 --- a/examples/complex_example.cpp +++ b/examples/complex_example.cpp @@ -1,3 +1,12 @@ +#include + +#if BEMAN_SCAN_VIEW_USE_MODULES() + +import std; +import beman.scan_view; + +#else + #include #include #include @@ -13,6 +22,8 @@ #include +#endif + namespace std { string to_string(string_view str) { return string{str}; } } // namespace std From 811bc6d9ff092fba6eb2672b5eaa3e651cb0d81b Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 15:05:40 -0400 Subject: [PATCH 05/26] Added new line --- include/beman/scan_view/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/beman/scan_view/CMakeLists.txt b/include/beman/scan_view/CMakeLists.txt index 115d255..3c21629 100644 --- a/include/beman/scan_view/CMakeLists.txt +++ b/include/beman/scan_view/CMakeLists.txt @@ -23,4 +23,4 @@ else() scan.hpp "${PROJECT_BINARY_DIR}/include/beman/scan_view/config_generated.hpp" ) -endif() \ No newline at end of file +endif() From f316a4852c18926e6eaa2117409261f2c66cb202 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 15:32:15 -0400 Subject: [PATCH 06/26] Fixed test in Clang --- tests/beman/scan_view/basic.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index 61a1a5a..20457c7 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -6,6 +6,7 @@ #include +import std; import beman.scan_view; #else From 6ac8a9053de7ce0a97276335a8ac35faef9e9995 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 16:00:37 -0400 Subject: [PATCH 07/26] CI for modules --- .github/workflows/ci_tests.yml | 10 ++++++++-- port/portfile.cmake.in | 17 +++++++++++++---- port/vcpkg.json.in | 7 ++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index b79dae7..0cd1a6e 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -44,7 +44,7 @@ jobs: "tests": [ "Debug.Default", "Release.Default", "Release.TSan", "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", - "Debug.Coverage" + "Debug.Coverage", "Debug.-DBEMAN_EXEMPLAR_USE_MODULES=On" ] } ] @@ -78,7 +78,8 @@ jobs: { "stdlibs": ["libstdc++", "libc++"], "tests": [ "Debug.Default", "Release.Default", "Release.TSan", - "Release.MaxSan", "Debug.Werror", "Debug.Dynamic" + "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", + "Debug.-DBEMAN_EXEMPLAR_USE_MODULES=On" ] } ] @@ -138,6 +139,11 @@ jobs: uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-ci.yml@1.6.0 with: port_name: beman-scan-view + feature_combinations: | + [ + {"features": {}}, + {"features": {"modules": true}} + ] create-issue-when-fault: needs: [preset-test, build-and-test] diff --git a/port/portfile.cmake.in b/port/portfile.cmake.in index da379f2..f008d49 100644 --- a/port/portfile.cmake.in +++ b/port/portfile.cmake.in @@ -6,9 +6,16 @@ vcpkg_from_github( HEAD_REF main ) +vcpkg_check_features( + OUT_FEATURE_OPTIONS FEATURE_OPTIONS + FEATURES + modules BEMAN_SCAN_VIEW_USE_MODULES +) + vcpkg_cmake_configure( SOURCE_PATH "${SOURCE_PATH}" OPTIONS + ${FEATURE_OPTIONS} -DBEMAN_SCAN_VIEW_BUILD_TESTS=OFF -DBEMAN_SCAN_VIEW_BUILD_EXAMPLES=OFF ) @@ -20,9 +27,11 @@ vcpkg_cmake_config_fixup( CONFIG_PATH lib/cmake/beman.scan_view ) -file(REMOVE_RECURSE - "${CURRENT_PACKAGES_DIR}/debug" - "${CURRENT_PACKAGES_DIR}/lib" -) +if(NOT "modules" IN_LIST FEATURES) + file(REMOVE_RECURSE + "${CURRENT_PACKAGES_DIR}/debug" + "${CURRENT_PACKAGES_DIR}/lib" + ) +endif() vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") diff --git a/port/vcpkg.json.in b/port/vcpkg.json.in index aee3e92..94dd030 100644 --- a/port/vcpkg.json.in +++ b/port/vcpkg.json.in @@ -13,5 +13,10 @@ "name": "vcpkg-cmake-config", "host": true } - ] + ], + "features": { + "modules": { + "description": "Provide beman.scan_view as a C++ module" + } + } } From fa8b78bf0ed722b3d008427f2558ab0640e46d4a Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk <91508478+yesmanchyk@users.noreply.github.com> Date: Sun, 10 May 2026 16:22:30 -0400 Subject: [PATCH 08/26] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CMakeLists.txt | 6 +++++- examples/basic_example.cpp | 8 ++++---- tests/beman/scan_view/basic.test.cpp | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f35273..d9faef2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,11 @@ option( ${PROJECT_IS_TOP_LEVEL} ) -option(BEMAN_SCAN_VIEW_USE_MODULES "Provide beman.scan_view as a C++ module" OFF) +option( + BEMAN_SCAN_VIEW_USE_MODULES + "Provide beman.scan_view as a C++ module" + OFF +) if(BEMAN_SCAN_VIEW_USE_MODULES) set(CMAKE_CXX_SCAN_FOR_MODULES ON) diff --git a/examples/basic_example.cpp b/examples/basic_example.cpp index a40c0ce..6ced35d 100644 --- a/examples/basic_example.cpp +++ b/examples/basic_example.cpp @@ -7,15 +7,15 @@ import beman.scan_view; #else -#include -#include -#include + #include + #include + #include #if __cpp_lib_print >= 202207L #include #endif -#include + #include #endif diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index 20457c7..9e9515c 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -4,7 +4,7 @@ #if BEMAN_SCAN_VIEW_USE_MODULES() -#include + #include import std; import beman.scan_view; @@ -18,7 +18,7 @@ import beman.scan_view; #include -#include + #include #endif From 649eebf0dba1184caed973c9a2fee3d92f1bd6b1 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk <91508478+yesmanchyk@users.noreply.github.com> Date: Sun, 10 May 2026 16:24:55 -0400 Subject: [PATCH 09/26] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- examples/basic_example.cpp | 6 +++--- tests/beman/scan_view/basic.test.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/basic_example.cpp b/examples/basic_example.cpp index 6ced35d..fdf1dff 100644 --- a/examples/basic_example.cpp +++ b/examples/basic_example.cpp @@ -11,9 +11,9 @@ import beman.scan_view; #include #include -#if __cpp_lib_print >= 202207L - #include -#endif + #if __cpp_lib_print >= 202207L + #include + #endif #include diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index 9e9515c..0ed2295 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -16,7 +16,7 @@ import beman.scan_view; #include #include -#include + #include #include From 1e39a97797fafd5be38facc650d8540d1e423377 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 16:29:25 -0400 Subject: [PATCH 10/26] Fix lint --- tests/beman/scan_view/basic.test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index 0ed2295..5e9b457 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -11,10 +11,10 @@ import beman.scan_view; #else -#include -#include -#include -#include + #include + #include + #include + #include #include From 15fcb10d48bd3b0c6fafc1f5fb498c7b544abd55 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 16:29:44 -0400 Subject: [PATCH 11/26] Fix lint --- examples/complex_example.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/complex_example.cpp b/examples/complex_example.cpp index a78c9d8..7cc152f 100644 --- a/examples/complex_example.cpp +++ b/examples/complex_example.cpp @@ -7,20 +7,20 @@ import beman.scan_view; #else -#include -#include -#include -#include -#include -#include -#include -#include - -#if __cpp_lib_print >= 202207L - #include -#endif - -#include + #include + #include + #include + #include + #include + #include + #include + #include + + #if __cpp_lib_print >= 202207L + #include + #endif + + #include #endif From 003dd37af71bf417b1ea9e52b6e1dc32a6f8b2d8 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 17:05:01 -0400 Subject: [PATCH 12/26] CI version bump --- .github/workflows/ci_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 0cd1a6e..4cbc527 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -136,7 +136,7 @@ jobs: } vcpkg-ci: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-ci.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-ci.yml@1.7.1 with: port_name: beman-scan-view feature_combinations: | From f75a6d19104fe1c99aadfe7ab0995c757bf38f5c Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 17:31:19 -0400 Subject: [PATCH 13/26] infra-workflows bump --- .github/workflows/ci_tests.yml | 8 ++++---- .github/workflows/pre-commit-check.yml | 2 +- .github/workflows/vcpkg-release.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 4cbc527..c4d5e72 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -13,10 +13,10 @@ on: jobs: beman-submodule-check: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.7.2 preset-test: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.7.2 with: matrix_config: > [ @@ -31,7 +31,7 @@ jobs: ] build-and-test: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.7.2 with: matrix_config: > { @@ -148,4 +148,4 @@ jobs: create-issue-when-fault: needs: [preset-test, build-and-test] if: failure() && github.event_name == 'schedule' - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.7.2 diff --git a/.github/workflows/pre-commit-check.yml b/.github/workflows/pre-commit-check.yml index 15c5280..6ec2693 100644 --- a/.github/workflows/pre-commit-check.yml +++ b/.github/workflows/pre-commit-check.yml @@ -16,4 +16,4 @@ permissions: jobs: pre-commit: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.7.2 diff --git a/.github/workflows/vcpkg-release.yml b/.github/workflows/vcpkg-release.yml index cbb6b66..11374a2 100644 --- a/.github/workflows/vcpkg-release.yml +++ b/.github/workflows/vcpkg-release.yml @@ -6,7 +6,7 @@ on: types: [published] jobs: vcpkg-release: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-release.yml@1.6.0 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-release.yml@1.7.2 with: port_name: beman-scan-view secrets: From 588de8ff334e8e89e30b1aaca6b329aa332439c8 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 17:41:00 -0400 Subject: [PATCH 14/26] Pass correct flag to use modules --- .github/workflows/ci_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index c4d5e72..3cce3e8 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -44,7 +44,7 @@ jobs: "tests": [ "Debug.Default", "Release.Default", "Release.TSan", "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", - "Debug.Coverage", "Debug.-DBEMAN_EXEMPLAR_USE_MODULES=On" + "Debug.Coverage", "Debug.-DBEMAN_SCAN_VIEW_USE_MODULES=On" ] } ] @@ -79,7 +79,7 @@ jobs: "tests": [ "Debug.Default", "Release.Default", "Release.TSan", "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", - "Debug.-DBEMAN_EXEMPLAR_USE_MODULES=On" + "Debug.-DBEMAN_SCAN_VIEW_USE_MODULES=On" ] } ] @@ -136,7 +136,7 @@ jobs: } vcpkg-ci: - uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-ci.yml@1.7.1 + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-vcpkg-ci.yml@1.7.2 with: port_name: beman-scan-view feature_combinations: | From 30d2e8590ced106d1b1532f0ef4b3b9fc5bad28f Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 17:55:57 -0400 Subject: [PATCH 15/26] Revert "Split scan_view implementation from dependencies" This reverts commit a18042bf8aab0050b1b5d67043340336f6decfe2. --- include/beman/scan_view/scan.hpp | 454 ++++++++++++++++++++++++- include/beman/scan_view/scan_view.hpp | 460 -------------------------- 2 files changed, 453 insertions(+), 461 deletions(-) delete mode 100644 include/beman/scan_view/scan_view.hpp diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index 4188e90..88c6f4f 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -11,6 +11,458 @@ #include #include -#include +namespace beman::scan_view { + +namespace detail { + +// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not +// just move-constructible; we preserve the old behavior in pre-C++23 modes. +template +concept movable_box_object = +#if __cpp_lib_ranges >= 202207L + std::move_constructible +#else + std::copy_constructible +#endif + && std::is_object_v; + +// Primary template - uses std::optional and introduces an empty state in case assignment fails. +template +class movable_box { + [[no_unique_address]] std::optional val_; + + public: + template + requires std::is_constructible_v + constexpr explicit movable_box(std::in_place_t, + Args&&... args) noexcept(std::is_nothrow_constructible_v) + : val_(std::in_place, std::forward(args)...) {} + + constexpr movable_box() noexcept(std::is_nothrow_default_constructible_v) + requires std::default_initializable + : val_(std::in_place) {} + + movable_box(const movable_box&) = default; + movable_box(movable_box&&) = default; + + constexpr movable_box& operator=(const movable_box& other) noexcept(std::is_nothrow_copy_constructible_v) +#if __cpp_lib_ranges >= 202207L + requires std::copy_constructible +#endif + { + if (this != std::addressof(other)) { + if (other.has_value()) + val_.emplace(*other); + else + val_.reset(); + } + return *this; + } + + movable_box& operator=(movable_box&&) + requires std::movable + = default; + + constexpr movable_box& operator=(movable_box&& other) noexcept(std::is_nothrow_move_constructible_v) { + if (this != std::addressof(other)) { + if (other.has_value()) + val_.emplace(std::move(*other)); + else + val_.reset(); + } + return *this; + } + + constexpr const Tp& operator*() const noexcept { return *val_; } + constexpr Tp& operator*() noexcept { return *val_; } + + constexpr const Tp* operator->() const noexcept { return val_.operator->(); } + constexpr Tp* operator->() noexcept { return val_.operator->(); } + + [[nodiscard]] constexpr bool has_value() const noexcept { return val_.has_value(); } +}; + +template +using maybe_const = std::conditional_t; + +template +struct perfect_forward_impl; + +template +struct perfect_forward_impl, BoundArgs...> { + private: + std::tuple bound_args_; + + public: + template , Args&&...>>> + explicit constexpr perfect_forward_impl(Args&&... bound_args) : bound_args_(std::forward(bound_args)...) {} + + perfect_forward_impl(const perfect_forward_impl&) = default; + perfect_forward_impl(perfect_forward_impl&&) = default; + + perfect_forward_impl& operator=(const perfect_forward_impl&) = default; + perfect_forward_impl& operator=(perfect_forward_impl&&) = default; + + template >> + constexpr auto + operator()(Args&&... args) & noexcept(noexcept(Op()(std::get(bound_args_)..., std::forward(args)...))) + -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { + return Op()(std::get(bound_args_)..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) & = delete; + + template >> + constexpr auto operator()(Args&&... args) const& noexcept(noexcept(Op()(std::get(bound_args_)..., + std::forward(args)...))) + -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { + return Op()(std::get(bound_args_)..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) const& = delete; + + template >> + constexpr auto operator()(Args&&... args) && noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., + std::forward(args)...))) + -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { + return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) && = delete; + + template >> + constexpr auto operator()(Args&&... args) const&& noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., + std::forward(args)...))) + -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { + return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); + } + + template >> + auto operator()(Args&&...) const&& = delete; +}; + +// perfect_forward implements a perfect-forwarding call wrapper as explained in [func.require]. +template +using perfect_forward = perfect_forward_impl, Args...>; + +struct compose_op { + template + constexpr auto operator()(Fn1&& f1, Fn2&& f2, Args&&... args) const + noexcept(noexcept(std::invoke(std::forward(f1), + std::invoke(std::forward(f2), std::forward(args)...)))) + -> decltype(std::invoke(std::forward(f1), + std::invoke(std::forward(f2), std::forward(args)...))) { + return std::invoke(std::forward(f1), std::invoke(std::forward(f2), std::forward(args)...)); + } +}; + +template +struct compose_t : perfect_forward { + using perfect_forward::perfect_forward; +}; + +template +constexpr auto compose(Fn1&& f1, Fn2&& f2) noexcept( + noexcept(compose_t, std::decay_t>(std::forward(f1), std::forward(f2)))) + -> decltype(compose_t, std::decay_t>(std::forward(f1), std::forward(f2))) { + return compose_t, std::decay_t>(std::forward(f1), std::forward(f2)); +} + +// CRTP base that one can derive from in order to be considered a range adaptor closure +// by the library. When deriving from this class, a pipe operator will be provided to +// make the following hold: +// - `x | f` is equivalent to `f(x)` +// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))` +template +struct range_adaptor_closure; + +// Type that wraps an arbitrary function object and makes it into a range adaptor closure, +// i.e. something that can be called via the `x | f` notation. +template +struct range_adaptor_closure_t : Fn, range_adaptor_closure> { + constexpr explicit range_adaptor_closure_t(Fn&& f) : Fn(std::move(f)) {} +}; + +template +concept RangeAdaptorClosure = + std::derived_from, range_adaptor_closure>>; + +template +struct range_adaptor_closure { + template + requires std::same_as> && std::invocable + [[nodiscard]] friend constexpr decltype(auto) + operator|(View&& view, Closure&& closure) noexcept(std::is_nothrow_invocable_v) { + return std::invoke(std::forward(closure), std::forward(view)); + } + + template + requires std::same_as> && + std::constructible_from, Closure> && + std::constructible_from, OtherClosure> + [[nodiscard]] friend constexpr auto + operator|(Closure&& c1, + OtherClosure&& c2) noexcept(std::is_nothrow_constructible_v, Closure> && + std::is_nothrow_constructible_v, OtherClosure>) { + return range_adaptor_closure_t(compose(std::forward(c2), std::forward(c1))); + } +}; + +template > +struct bind_back_op; + +template +struct bind_back_op> { + template + constexpr auto operator()(Fn&& f, BoundArgs&& bound_args, Args&&... args) const noexcept(noexcept(std::invoke( + std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...))) + -> decltype(std::invoke(std::forward(f), + std::forward(args)..., + std::get(std::forward(bound_args))...)) { + return std::invoke( + std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...); + } +}; + +template +struct bind_back_t : perfect_forward>, Fn, BoundArgs> { + using perfect_forward>, Fn, BoundArgs>::perfect_forward; +}; + +template + requires std::is_constructible_v, Fn> && std::is_move_constructible_v> && + (std::is_constructible_v, Args> && ...) && + (std::is_move_constructible_v> && ...) +constexpr auto +bind_back(Fn&& f, Args&&... args) noexcept(noexcept(bind_back_t, std::tuple...>>( + std::forward(f), std::forward_as_tuple(std::forward(args)...)))) + -> decltype(bind_back_t, std::tuple...>>( + std::forward(f), std::forward_as_tuple(std::forward(args)...))) { + return bind_back_t, std::tuple...>>( + std::forward(f), std::forward_as_tuple(std::forward(args)...)); +} + +template +constexpr bool tidy_func = + std::is_empty_v && std::is_trivially_default_constructible_v && std::is_trivially_destructible_v; + +} // namespace detail + +enum class scan_view_kind : bool { unseeded, seeded }; + +template +concept scannable_impl = // exposition only + std::movable && std::convertible_to && std::invocable> && + std::assignable_from>>; + +template +concept scannable = // exposition only + std::invocable> && + std::convertible_to>, + std::decay_t>>> && + scannable_impl>>>; + +template + requires std::ranges::view && std::is_object_v && std::is_object_v && scannable +class scan_view : public std::ranges::view_interface> { + private: + // [range.scan.iterator], class template scan_view::iterator + template + class iterator; // exposition only + + template + friend class iterator; // exposition only + + V base_ = V(); // exposition only + detail::movable_box fun_; // exposition only + detail::movable_box init_; // exposition only + + public: + scan_view() + requires std::default_initializable && std::default_initializable + = default; + constexpr explicit scan_view(V base, F fun) + requires(K == scan_view_kind::unseeded) + : base_{std::move(base)}, fun_{std::in_place, std::move(fun)} {} + constexpr explicit scan_view(V base, F fun, T init) + requires(K == scan_view_kind::seeded) + : base_{std::move(base)}, fun_{std::in_place, std::move(fun)}, init_{std::in_place, std::move(init)} {} + + constexpr V base() const& + requires std::copy_constructible + { + return base_; + } + constexpr V base() && { return std::move(base_); } + + constexpr iterator begin() { return iterator{*this, std::ranges::begin(base_)}; } + constexpr iterator begin() const + requires std::ranges::range && scannable + { + return iterator{*this, std::ranges::begin(base_)}; + } + + [[nodiscard]] constexpr std::default_sentinel_t end() const noexcept { return std::default_sentinel; } + + constexpr auto size() + requires std::ranges::sized_range + { + return std::ranges::size(base_); + } + constexpr auto size() const + requires std::ranges::sized_range + { + return std::ranges::size(base_); + } + +#if __cpp_lib_ranges_reserve_hint >= 202502L + constexpr auto reserve_hint() + requires std::ranges::approximately_sized_range + { + return std::ranges::reserve_hint(base_); + } + + constexpr auto reserve_hint() const + requires std::ranges::approximately_sized_range + { + return std::ranges::reserve_hint(base_); + } +#endif +}; + +template +scan_view(R&&, F) -> scan_view, F, std::ranges::range_value_t>; +template +scan_view(R&&, F, T) -> scan_view, F, T, scan_view_kind::seeded>; + +template + requires std::ranges::view && std::is_object_v && std::is_object_v && scannable +template +class scan_view::iterator { + private: + using Parent = detail::maybe_const; // exposition only + using Base = detail::maybe_const; // exposition only + using Func = detail::maybe_const; // exposition only + using ResultType = std::decay_t< // exposition only + std::invoke_result_t>>; + + struct Holder { // exposition only + std::ranges::sentinel_t end_ = std::ranges::sentinel_t(); // exposition only + detail::movable_box fun_; // exposition only + detail::movable_box init_; // exposition only + }; + using HolderType = std::conditional_t, Holder, Parent*>; // exposition only + + std::ranges::iterator_t current_ = std::ranges::iterator_t(); // exposition only + HolderType parent_ = {}; // exposition only + detail::movable_box sum_; // exposition only + + constexpr std::ranges::sentinel_t get_end() const { // exposition only + if constexpr (detail::tidy_func) + return parent_.end_; + else + return std::ranges::end(parent_->base_); + } + constexpr Func& get_fun() { // exposition only + if constexpr (detail::tidy_func) + return *parent_.fun_; + else + return *parent_->fun_; + } + constexpr T& get_init() { // exposition only + if constexpr (detail::tidy_func) + return *parent_.init_; + else + return *parent_->init_; + } + static constexpr HolderType init(Parent& parent) { // exposition only + if constexpr (detail::tidy_func) + return {std::ranges::end(parent.base_), detail::movable_box{std::in_place}, parent.init_}; + else + return std::addressof(parent); + } + + public: + using iterator_concept = std::input_iterator_tag; + using value_type = ResultType; + using difference_type = std::ranges::range_difference_t; + + iterator() + requires std::default_initializable> + = default; + constexpr iterator(Parent& parent, std::ranges::iterator_t current) + : current_{std::move(current)}, parent_{init(parent)} { + if (current_ == get_end()) + return; + if constexpr (K == scan_view_kind::seeded) { + sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), get_init(), *current_)}; + } else { + sum_ = detail::movable_box{std::in_place, *current_}; + } + } + constexpr iterator(iterator i) + requires Const && std::convertible_to, std::ranges::iterator_t> + : current_{std::move(i.current_)}, parent_{std::move(i.parent_)}, sum_{std::move(i.sum_)} {} + + constexpr const std::ranges::iterator_t& base() const& noexcept { return current_; } + constexpr std::ranges::iterator_t base() && { return std::move(current_); } + + constexpr const value_type& operator*() const { return *sum_; } + + constexpr iterator& operator++() { + if (++current_ != get_end()) { + sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), std::move(*sum_), *current_)}; + } + return *this; + } + constexpr void operator++(int) { ++*this; } + + friend constexpr bool operator==(const iterator& x, const iterator& y) + requires std::equality_comparable> + { + return x.current_ == y.current_; + } + friend constexpr bool operator==(const iterator& x, std::default_sentinel_t) { return x.current_ == x.get_end(); } +}; + +namespace detail { + +struct scan_t { + constexpr scan_t() = default; + constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F) const { + return scan_view{std::forward(E), std::forward(F)}; + } + constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F, auto&& G) const { + return scan_view{std::forward(E), std::forward(F), std::forward(G)}; + } + + constexpr auto operator()(auto&& E) const { + return detail::range_adaptor_closure_t(detail::bind_back(*this, std::forward(E))); + } + constexpr auto operator()(auto&& E, auto&& F) const { + return detail::range_adaptor_closure_t( + detail::bind_back(*this, std::forward(E), std::forward(F))); + } +}; + +} // namespace detail + +inline constexpr detail::scan_t scan{}; + +} // namespace beman::scan_view + +// Conditionally borrowed range (P3117) +template +constexpr bool std::ranges::enable_borrowed_range> = + std::ranges::enable_borrowed_range && beman::scan_view::detail::tidy_func; #endif // BEMAN_SCAN_VIEW_SCAN_HPP diff --git a/include/beman/scan_view/scan_view.hpp b/include/beman/scan_view/scan_view.hpp deleted file mode 100644 index 9502bfb..0000000 --- a/include/beman/scan_view/scan_view.hpp +++ /dev/null @@ -1,460 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#ifndef BEMAN_SCAN_VIEW_SCAN_VIEW_HPP -#define BEMAN_SCAN_VIEW_SCAN_VIEW_HPP - -namespace beman::scan_view { - -namespace detail { - -// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not -// just move-constructible; we preserve the old behavior in pre-C++23 modes. -template -concept movable_box_object = -#if __cpp_lib_ranges >= 202207L - std::move_constructible -#else - std::copy_constructible -#endif - && std::is_object_v; - -// Primary template - uses std::optional and introduces an empty state in case assignment fails. -template -class movable_box { - [[no_unique_address]] std::optional val_; - - public: - template - requires std::is_constructible_v - constexpr explicit movable_box(std::in_place_t, - Args&&... args) noexcept(std::is_nothrow_constructible_v) - : val_(std::in_place, std::forward(args)...) {} - - constexpr movable_box() noexcept(std::is_nothrow_default_constructible_v) - requires std::default_initializable - : val_(std::in_place) {} - - movable_box(const movable_box&) = default; - movable_box(movable_box&&) = default; - - constexpr movable_box& operator=(const movable_box& other) noexcept(std::is_nothrow_copy_constructible_v) -#if __cpp_lib_ranges >= 202207L - requires std::copy_constructible -#endif - { - if (this != std::addressof(other)) { - if (other.has_value()) - val_.emplace(*other); - else - val_.reset(); - } - return *this; - } - - movable_box& operator=(movable_box&&) - requires std::movable - = default; - - constexpr movable_box& operator=(movable_box&& other) noexcept(std::is_nothrow_move_constructible_v) { - if (this != std::addressof(other)) { - if (other.has_value()) - val_.emplace(std::move(*other)); - else - val_.reset(); - } - return *this; - } - - constexpr const Tp& operator*() const noexcept { return *val_; } - constexpr Tp& operator*() noexcept { return *val_; } - - constexpr const Tp* operator->() const noexcept { return val_.operator->(); } - constexpr Tp* operator->() noexcept { return val_.operator->(); } - - [[nodiscard]] constexpr bool has_value() const noexcept { return val_.has_value(); } -}; - -template -using maybe_const = std::conditional_t; - -template -struct perfect_forward_impl; - -template -struct perfect_forward_impl, BoundArgs...> { - private: - std::tuple bound_args_; - - public: - template , Args&&...>>> - explicit constexpr perfect_forward_impl(Args&&... bound_args) : bound_args_(std::forward(bound_args)...) {} - - perfect_forward_impl(const perfect_forward_impl&) = default; - perfect_forward_impl(perfect_forward_impl&&) = default; - - perfect_forward_impl& operator=(const perfect_forward_impl&) = default; - perfect_forward_impl& operator=(perfect_forward_impl&&) = default; - - template >> - constexpr auto - operator()(Args&&... args) & noexcept(noexcept(Op()(std::get(bound_args_)..., std::forward(args)...))) - -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { - return Op()(std::get(bound_args_)..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) & = delete; - - template >> - constexpr auto operator()(Args&&... args) const& noexcept(noexcept(Op()(std::get(bound_args_)..., - std::forward(args)...))) - -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { - return Op()(std::get(bound_args_)..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) const& = delete; - - template >> - constexpr auto operator()(Args&&... args) && noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., - std::forward(args)...))) - -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { - return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) && = delete; - - template >> - constexpr auto operator()(Args&&... args) const&& noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., - std::forward(args)...))) - -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { - return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); - } - - template >> - auto operator()(Args&&...) const&& = delete; -}; - -// perfect_forward implements a perfect-forwarding call wrapper as explained in [func.require]. -template -using perfect_forward = perfect_forward_impl, Args...>; - -struct compose_op { - template - constexpr auto operator()(Fn1&& f1, Fn2&& f2, Args&&... args) const - noexcept(noexcept(std::invoke(std::forward(f1), - std::invoke(std::forward(f2), std::forward(args)...)))) - -> decltype(std::invoke(std::forward(f1), - std::invoke(std::forward(f2), std::forward(args)...))) { - return std::invoke(std::forward(f1), std::invoke(std::forward(f2), std::forward(args)...)); - } -}; - -template -struct compose_t : perfect_forward { - using perfect_forward::perfect_forward; -}; - -template -constexpr auto compose(Fn1&& f1, Fn2&& f2) noexcept( - noexcept(compose_t, std::decay_t>(std::forward(f1), std::forward(f2)))) - -> decltype(compose_t, std::decay_t>(std::forward(f1), std::forward(f2))) { - return compose_t, std::decay_t>(std::forward(f1), std::forward(f2)); -} - -// CRTP base that one can derive from in order to be considered a range adaptor closure -// by the library. When deriving from this class, a pipe operator will be provided to -// make the following hold: -// - `x | f` is equivalent to `f(x)` -// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))` -template -struct range_adaptor_closure; - -// Type that wraps an arbitrary function object and makes it into a range adaptor closure, -// i.e. something that can be called via the `x | f` notation. -template -struct range_adaptor_closure_t : Fn, range_adaptor_closure> { - constexpr explicit range_adaptor_closure_t(Fn&& f) : Fn(std::move(f)) {} -}; - -template -concept RangeAdaptorClosure = - std::derived_from, range_adaptor_closure>>; - -template -struct range_adaptor_closure { - template - requires std::same_as> && std::invocable - [[nodiscard]] friend constexpr decltype(auto) - operator|(View&& view, Closure&& closure) noexcept(std::is_nothrow_invocable_v) { - return std::invoke(std::forward(closure), std::forward(view)); - } - - template - requires std::same_as> && - std::constructible_from, Closure> && - std::constructible_from, OtherClosure> - [[nodiscard]] friend constexpr auto - operator|(Closure&& c1, - OtherClosure&& c2) noexcept(std::is_nothrow_constructible_v, Closure> && - std::is_nothrow_constructible_v, OtherClosure>) { - return range_adaptor_closure_t(compose(std::forward(c2), std::forward(c1))); - } -}; - -template > -struct bind_back_op; - -template -struct bind_back_op> { - template - constexpr auto operator()(Fn&& f, BoundArgs&& bound_args, Args&&... args) const noexcept(noexcept(std::invoke( - std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...))) - -> decltype(std::invoke(std::forward(f), - std::forward(args)..., - std::get(std::forward(bound_args))...)) { - return std::invoke( - std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...); - } -}; - -template -struct bind_back_t : perfect_forward>, Fn, BoundArgs> { - using perfect_forward>, Fn, BoundArgs>::perfect_forward; -}; - -template - requires std::is_constructible_v, Fn> && std::is_move_constructible_v> && - (std::is_constructible_v, Args> && ...) && - (std::is_move_constructible_v> && ...) -constexpr auto -bind_back(Fn&& f, Args&&... args) noexcept(noexcept(bind_back_t, std::tuple...>>( - std::forward(f), std::forward_as_tuple(std::forward(args)...)))) - -> decltype(bind_back_t, std::tuple...>>( - std::forward(f), std::forward_as_tuple(std::forward(args)...))) { - return bind_back_t, std::tuple...>>( - std::forward(f), std::forward_as_tuple(std::forward(args)...)); -} - -template -constexpr bool tidy_func = - std::is_empty_v && std::is_trivially_default_constructible_v && std::is_trivially_destructible_v; - -} // namespace detail - -enum class scan_view_kind : bool { unseeded, seeded }; - -template -concept scannable_impl = // exposition only - std::movable && std::convertible_to && std::invocable> && - std::assignable_from>>; - -template -concept scannable = // exposition only - std::invocable> && - std::convertible_to>, - std::decay_t>>> && - scannable_impl>>>; - -template - requires std::ranges::view && std::is_object_v && std::is_object_v && scannable -class scan_view : public std::ranges::view_interface> { - private: - // [range.scan.iterator], class template scan_view::iterator - template - class iterator; // exposition only - - template - friend class iterator; // exposition only - - V base_ = V(); // exposition only - detail::movable_box fun_; // exposition only - detail::movable_box init_; // exposition only - - public: - scan_view() - requires std::default_initializable && std::default_initializable - = default; - constexpr explicit scan_view(V base, F fun) - requires(K == scan_view_kind::unseeded) - : base_{std::move(base)}, fun_{std::in_place, std::move(fun)} {} - constexpr explicit scan_view(V base, F fun, T init) - requires(K == scan_view_kind::seeded) - : base_{std::move(base)}, fun_{std::in_place, std::move(fun)}, init_{std::in_place, std::move(init)} {} - - constexpr V base() const& - requires std::copy_constructible - { - return base_; - } - constexpr V base() && { return std::move(base_); } - - constexpr iterator begin() { return iterator{*this, std::ranges::begin(base_)}; } - constexpr iterator begin() const - requires std::ranges::range && scannable - { - return iterator{*this, std::ranges::begin(base_)}; - } - - [[nodiscard]] constexpr std::default_sentinel_t end() const noexcept { return std::default_sentinel; } - - constexpr auto size() - requires std::ranges::sized_range - { - return std::ranges::size(base_); - } - constexpr auto size() const - requires std::ranges::sized_range - { - return std::ranges::size(base_); - } - -#if __cpp_lib_ranges_reserve_hint >= 202502L - constexpr auto reserve_hint() - requires std::ranges::approximately_sized_range - { - return std::ranges::reserve_hint(base_); - } - - constexpr auto reserve_hint() const - requires std::ranges::approximately_sized_range - { - return std::ranges::reserve_hint(base_); - } -#endif -}; - -template -scan_view(R&&, F) -> scan_view, F, std::ranges::range_value_t>; -template -scan_view(R&&, F, T) -> scan_view, F, T, scan_view_kind::seeded>; - -template - requires std::ranges::view && std::is_object_v && std::is_object_v && scannable -template -class scan_view::iterator { - private: - using Parent = detail::maybe_const; // exposition only - using Base = detail::maybe_const; // exposition only - using Func = detail::maybe_const; // exposition only - using ResultType = std::decay_t< // exposition only - std::invoke_result_t>>; - - struct Holder { // exposition only - std::ranges::sentinel_t end_ = std::ranges::sentinel_t(); // exposition only - detail::movable_box fun_; // exposition only - detail::movable_box init_; // exposition only - }; - using HolderType = std::conditional_t, Holder, Parent*>; // exposition only - - std::ranges::iterator_t current_ = std::ranges::iterator_t(); // exposition only - HolderType parent_ = {}; // exposition only - detail::movable_box sum_; // exposition only - - constexpr std::ranges::sentinel_t get_end() const { // exposition only - if constexpr (detail::tidy_func) - return parent_.end_; - else - return std::ranges::end(parent_->base_); - } - constexpr Func& get_fun() { // exposition only - if constexpr (detail::tidy_func) - return *parent_.fun_; - else - return *parent_->fun_; - } - constexpr T& get_init() { // exposition only - if constexpr (detail::tidy_func) - return *parent_.init_; - else - return *parent_->init_; - } - static constexpr HolderType init(Parent& parent) { // exposition only - if constexpr (detail::tidy_func) - return {std::ranges::end(parent.base_), detail::movable_box{std::in_place}, parent.init_}; - else - return std::addressof(parent); - } - - public: - using iterator_concept = std::input_iterator_tag; - using value_type = ResultType; - using difference_type = std::ranges::range_difference_t; - - iterator() - requires std::default_initializable> - = default; - constexpr iterator(Parent& parent, std::ranges::iterator_t current) - : current_{std::move(current)}, parent_{init(parent)} { - if (current_ == get_end()) - return; - if constexpr (K == scan_view_kind::seeded) { - sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), get_init(), *current_)}; - } else { - sum_ = detail::movable_box{std::in_place, *current_}; - } - } - constexpr iterator(iterator i) - requires Const && std::convertible_to, std::ranges::iterator_t> - : current_{std::move(i.current_)}, parent_{std::move(i.parent_)}, sum_{std::move(i.sum_)} {} - - constexpr const std::ranges::iterator_t& base() const& noexcept { return current_; } - constexpr std::ranges::iterator_t base() && { return std::move(current_); } - - constexpr const value_type& operator*() const { return *sum_; } - - constexpr iterator& operator++() { - if (++current_ != get_end()) { - sum_ = detail::movable_box{std::in_place, std::invoke(get_fun(), std::move(*sum_), *current_)}; - } - return *this; - } - constexpr void operator++(int) { ++*this; } - - friend constexpr bool operator==(const iterator& x, const iterator& y) - requires std::equality_comparable> - { - return x.current_ == y.current_; - } - friend constexpr bool operator==(const iterator& x, std::default_sentinel_t) { return x.current_ == x.get_end(); } -}; - -namespace detail { - -struct scan_t { - constexpr scan_t() = default; - constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F) const { - return scan_view{std::forward(E), std::forward(F)}; - } - constexpr auto operator()(std::ranges::input_range auto&& E, auto&& F, auto&& G) const { - return scan_view{std::forward(E), std::forward(F), std::forward(G)}; - } - - constexpr auto operator()(auto&& E) const { - return detail::range_adaptor_closure_t(detail::bind_back(*this, std::forward(E))); - } - constexpr auto operator()(auto&& E, auto&& F) const { - return detail::range_adaptor_closure_t( - detail::bind_back(*this, std::forward(E), std::forward(F))); - } -}; - -} // namespace detail - -inline constexpr detail::scan_t scan{}; - -} // namespace beman::scan_view - -// Conditionally borrowed range (P3117) -template -constexpr bool std::ranges::enable_borrowed_range> = - std::ranges::enable_borrowed_range && beman::scan_view::detail::tidy_func; - -#endif // BEMAN_SCAN_VIEW_SCAN_VIEW_HPP From 12130dea7d7633f2677edd31920cb0af0da53394 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 17:58:42 -0400 Subject: [PATCH 16/26] Remove reverted file --- include/beman/scan_view/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/beman/scan_view/CMakeLists.txt b/include/beman/scan_view/CMakeLists.txt index 3c21629..638d1f1 100644 --- a/include/beman/scan_view/CMakeLists.txt +++ b/include/beman/scan_view/CMakeLists.txt @@ -8,7 +8,6 @@ if(BEMAN_SCAN_VIEW_USE_MODULES) FILE_SET HEADERS FILES config.hpp - scan_view.hpp scan.hpp "${PROJECT_BINARY_DIR}/include/beman/scan_view/config_generated.hpp" ) @@ -19,7 +18,6 @@ else() FILE_SET HEADERS FILES config.hpp - scan_view.hpp scan.hpp "${PROJECT_BINARY_DIR}/include/beman/scan_view/config_generated.hpp" ) From f3ca98b6edcd94266f2a29f6d9d644224661c1ac Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 18:09:37 -0400 Subject: [PATCH 17/26] Use compatibility headers --- include/beman/scan_view/scan.hpp | 16 ++++++++++++++++ include/beman/scan_view/scan_view.cppm | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index 88c6f4f..bb1cef8 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -3,6 +3,17 @@ #ifndef BEMAN_SCAN_VIEW_SCAN_HPP #define BEMAN_SCAN_VIEW_SCAN_HPP +#include + +#if BEMAN_SCAN_VIEW_USE_MODULES() && \ + !defined(BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT) + +import beman.scan_view; + +#else + +#if !BEMAN_SCAN_VIEW_USE_MODULES() + #include #include #include @@ -11,6 +22,11 @@ #include #include +#endif // !BEMAN_SCAN_VIEW_USE_MODULES() + +#endif // #if BEMAN_SCAN_VIEW_USE_MODULES() && + // !defined(BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT) + namespace beman::scan_view { namespace detail { diff --git a/include/beman/scan_view/scan_view.cppm b/include/beman/scan_view/scan_view.cppm index 17eb087..b22b956 100644 --- a/include/beman/scan_view/scan_view.cppm +++ b/include/beman/scan_view/scan_view.cppm @@ -14,6 +14,6 @@ export module beman.scan_view; export { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winclude-angled-in-module-purview" -#include +#include #pragma clang diagnostic pop } From 36a3452a589e3bfa0ed9d8d396f1f93df1b7d130 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Sun, 10 May 2026 18:33:03 -0400 Subject: [PATCH 18/26] Use compatibility headers in tests and examples --- examples/basic_example.cpp | 5 ++--- examples/complex_example.cpp | 5 ++--- include/beman/scan_view/scan.hpp | 6 +++--- tests/beman/scan_view/basic.test.cpp | 5 ++--- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/examples/basic_example.cpp b/examples/basic_example.cpp index fdf1dff..09db63f 100644 --- a/examples/basic_example.cpp +++ b/examples/basic_example.cpp @@ -3,7 +3,6 @@ #if BEMAN_SCAN_VIEW_USE_MODULES() import std; -import beman.scan_view; #else @@ -15,10 +14,10 @@ import beman.scan_view; #include #endif - #include - #endif +#include + namespace exe = beman::scan_view; #if __cpp_lib_print >= 202207L && __cpp_lib_format_ranges >= 202207L diff --git a/examples/complex_example.cpp b/examples/complex_example.cpp index 7cc152f..f29487b 100644 --- a/examples/complex_example.cpp +++ b/examples/complex_example.cpp @@ -3,7 +3,6 @@ #if BEMAN_SCAN_VIEW_USE_MODULES() import std; -import beman.scan_view; #else @@ -20,10 +19,10 @@ import beman.scan_view; #include #endif - #include - #endif +#include + namespace std { string to_string(string_view str) { return string{str}; } } // namespace std diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index bb1cef8..fe25697 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -24,9 +24,6 @@ import beman.scan_view; #endif // !BEMAN_SCAN_VIEW_USE_MODULES() -#endif // #if BEMAN_SCAN_VIEW_USE_MODULES() && - // !defined(BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT) - namespace beman::scan_view { namespace detail { @@ -481,4 +478,7 @@ template > = std::ranges::enable_borrowed_range && beman::scan_view::detail::tidy_func; +#endif // #if BEMAN_SCAN_VIEW_USE_MODULES() && + // !defined(BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT) + #endif // BEMAN_SCAN_VIEW_SCAN_HPP diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index 5e9b457..bec23bd 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -7,7 +7,6 @@ #include import std; -import beman.scan_view; #else @@ -18,10 +17,10 @@ import beman.scan_view; #include - #include - #endif +#include + namespace exe = beman::scan_view; template > From f196c4bef2268501d4494e6a9f9474dcc43946de Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Mon, 11 May 2026 17:57:13 -0400 Subject: [PATCH 19/26] Avoid global module fragment --- include/beman/scan_view/scan_view.cppm | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/include/beman/scan_view/scan_view.cppm b/include/beman/scan_view/scan_view.cppm index b22b956..4cb6fd3 100644 --- a/include/beman/scan_view/scan_view.cppm +++ b/include/beman/scan_view/scan_view.cppm @@ -1,14 +1,10 @@ -module; +export module beman.scan_view; -#include -#include -#include -#include -#include -#include -#include +import std; -export module beman.scan_view; +using size_t = std::size_t; + +#define __cpp_lib_ranges 202207L #define BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT export { From 3aef587d9cad9a26cb40cdf66ea29e396e7b05e5 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Mon, 11 May 2026 18:53:52 -0400 Subject: [PATCH 20/26] beman-submodule update --remote --- infra/.beman_submodule | 2 +- infra/.github/CODEOWNERS | 2 +- infra/.github/workflows/pre-commit.yml | 1 + infra/.pre-commit-config.yaml | 1 + infra/cmake/BuildTelemetry.cmake | 1 + infra/cmake/BuildTelemetryConfig.cmake | 1 + infra/cmake/Config.cmake.in | 2 +- infra/cmake/beman-install-library.cmake | 6 ++++-- infra/cmake/enable-experimental-import-std.cmake | 4 ---- infra/cmake/telemetry.sh | 1 + infra/cmake/use-fetch-content.cmake | 1 + 11 files changed, 13 insertions(+), 9 deletions(-) diff --git a/infra/.beman_submodule b/infra/.beman_submodule index a2ad45f..4e9900c 100644 --- a/infra/.beman_submodule +++ b/infra/.beman_submodule @@ -1,3 +1,3 @@ [beman_submodule] remote=https://github.com/bemanproject/infra.git -commit_hash=1b14bad2cd2cf0e44d1aeb608557e0e35ce27eaa +commit_hash=b7b533a00ba72049c7c583f4344dfb7c04342a0f diff --git a/infra/.github/CODEOWNERS b/infra/.github/CODEOWNERS index 4ff90a4..439303d 100644 --- a/infra/.github/CODEOWNERS +++ b/infra/.github/CODEOWNERS @@ -1 +1 @@ -* @ednolan @neatudarius @rishyak @wusatosi @JeffGarland +* @ednolan @rishyak @wusatosi @JeffGarland diff --git a/infra/.github/workflows/pre-commit.yml b/infra/.github/workflows/pre-commit.yml index 9646831..7051c13 100644 --- a/infra/.github/workflows/pre-commit.yml +++ b/infra/.github/workflows/pre-commit.yml @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Lint Check (pre-commit) on: diff --git a/infra/.pre-commit-config.yaml b/infra/.pre-commit-config.yaml index 8052e18..6fe1e85 100644 --- a/infra/.pre-commit-config.yaml +++ b/infra/.pre-commit-config.yaml @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 diff --git a/infra/cmake/BuildTelemetry.cmake b/infra/cmake/BuildTelemetry.cmake index c2ff343..cc94f40 100755 --- a/infra/cmake/BuildTelemetry.cmake +++ b/infra/cmake/BuildTelemetry.cmake @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception include_guard(GLOBAL) include(${CMAKE_CURRENT_LIST_DIR}/BuildTelemetryConfig.cmake) diff --git a/infra/cmake/BuildTelemetryConfig.cmake b/infra/cmake/BuildTelemetryConfig.cmake index 15aae48..2160c34 100755 --- a/infra/cmake/BuildTelemetryConfig.cmake +++ b/infra/cmake/BuildTelemetryConfig.cmake @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception include_guard(GLOBAL) set(BUILD_TELEMETRY_DIR ${CMAKE_CURRENT_LIST_DIR}) diff --git a/infra/cmake/Config.cmake.in b/infra/cmake/Config.cmake.in index 3f1341c..df903cf 100644 --- a/infra/cmake/Config.cmake.in +++ b/infra/cmake/Config.cmake.in @@ -1,5 +1,5 @@ -# cmake/Config.cmake.in -*-makefile-*- # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# cmake/Config.cmake.in -*-makefile-*- include(CMakeFindDependencyMacro) diff --git a/infra/cmake/beman-install-library.cmake b/infra/cmake/beman-install-library.cmake index dc5a4d1..df2dbe9 100644 --- a/infra/cmake/beman-install-library.cmake +++ b/infra/cmake/beman-install-library.cmake @@ -122,7 +122,9 @@ function(beman_install_library name) endif() if(NOT BEMAN_INSTALL_DESTINATION) - set(BEMAN_INSTALL_DESTINATION "${_config_install_dir}/modules") + set(BEMAN_INSTALL_DESTINATION + "${CMAKE_INSTALL_DATADIR}/${name}/modules" + ) endif() string(REPLACE "beman." "" install_component_name "${name}") @@ -210,7 +212,7 @@ function(beman_install_library name) # NOTE: There's currently no convention for this location! CK CXX_MODULES_BMI DESTINATION - ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ + ${CMAKE_INSTALL_DATADIR}/${name}/bmi-${CMAKE_CXX_COMPILER_ID}_$ COMPONENT "${install_component_name}_Development" ) else() diff --git a/infra/cmake/enable-experimental-import-std.cmake b/infra/cmake/enable-experimental-import-std.cmake index a45c699..20fc302 100644 --- a/infra/cmake/enable-experimental-import-std.cmake +++ b/infra/cmake/enable-experimental-import-std.cmake @@ -187,8 +187,4 @@ elseif(CMAKE_VERSION VERSION_EQUAL "4.3.2") set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "451f2fe2-a8a2-47c3-bc32-94786d8fc91b" ) -elseif(CMAKE_VERSION VERSION_EQUAL "4.3.20260507-g46410ea") - set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD - "451f2fe2-a8a2-47c3-bc32-94786d8fc91b" - ) endif() diff --git a/infra/cmake/telemetry.sh b/infra/cmake/telemetry.sh index 323982e..cb5fd88 100755 --- a/infra/cmake/telemetry.sh +++ b/infra/cmake/telemetry.sh @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #!/usr/bin/env bash set -o nounset diff --git a/infra/cmake/use-fetch-content.cmake b/infra/cmake/use-fetch-content.cmake index 73594be..3c7136d 100644 --- a/infra/cmake/use-fetch-content.cmake +++ b/infra/cmake/use-fetch-content.cmake @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception cmake_minimum_required(VERSION 3.24) include(FetchContent) From e7c1aad0a5e33c8ed60f1700f7ccbb4486a5b410 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk <91508478+yesmanchyk@users.noreply.github.com> Date: Mon, 11 May 2026 19:26:10 -0400 Subject: [PATCH 21/26] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- include/beman/scan_view/scan.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index fe25697..1d8740d 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -5,14 +5,13 @@ #include -#if BEMAN_SCAN_VIEW_USE_MODULES() && \ - !defined(BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT) +#if BEMAN_SCAN_VIEW_USE_MODULES() && !defined(BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT) import beman.scan_view; #else -#if !BEMAN_SCAN_VIEW_USE_MODULES() + #if !BEMAN_SCAN_VIEW_USE_MODULES() #include #include @@ -22,7 +21,7 @@ import beman.scan_view; #include #include -#endif // !BEMAN_SCAN_VIEW_USE_MODULES() + #endif // !BEMAN_SCAN_VIEW_USE_MODULES() namespace beman::scan_view { @@ -475,7 +474,7 @@ template -constexpr bool std::ranges::enable_borrowed_range> = +constexpr bool std::ranges::enable_borrowed_range > = std::ranges::enable_borrowed_range && beman::scan_view::detail::tidy_func; #endif // #if BEMAN_SCAN_VIEW_USE_MODULES() && From d41698d9a8f0172edd827ca4fad6d4ca0d8a0271 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Mon, 11 May 2026 19:31:00 -0400 Subject: [PATCH 22/26] Indent includes --- include/beman/scan_view/scan.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index 1d8740d..4057d3c 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -13,13 +13,13 @@ import beman.scan_view; #if !BEMAN_SCAN_VIEW_USE_MODULES() -#include -#include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include + #include + #include #endif // !BEMAN_SCAN_VIEW_USE_MODULES() From bb40384f9e5601dce8c4aa28238485142f842ae7 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Mon, 11 May 2026 19:53:06 -0400 Subject: [PATCH 23/26] clang-format -i include/beman/scan_view/scan.hpp --- include/beman/scan_view/scan.hpp | 88 ++++++++++++++++---------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index 4057d3c..e5ac628 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -31,11 +31,11 @@ namespace detail { // just move-constructible; we preserve the old behavior in pre-C++23 modes. template concept movable_box_object = -#if __cpp_lib_ranges >= 202207L + #if __cpp_lib_ranges >= 202207L std::move_constructible -#else + #else std::copy_constructible -#endif + #endif && std::is_object_v; // Primary template - uses std::optional and introduces an empty state in case assignment fails. @@ -58,9 +58,9 @@ class movable_box { movable_box(movable_box&&) = default; constexpr movable_box& operator=(const movable_box& other) noexcept(std::is_nothrow_copy_constructible_v) -#if __cpp_lib_ranges >= 202207L + #if __cpp_lib_ranges >= 202207L requires std::copy_constructible -#endif + #endif { if (this != std::addressof(other)) { if (other.has_value()) @@ -106,7 +106,7 @@ struct perfect_forward_impl, BoundArgs...> { std::tuple bound_args_; public: - template , Args&&...>>> + template , Args&&...> > > explicit constexpr perfect_forward_impl(Args&&... bound_args) : bound_args_(std::forward(bound_args)...) {} perfect_forward_impl(const perfect_forward_impl&) = default; @@ -115,44 +115,44 @@ struct perfect_forward_impl, BoundArgs...> { perfect_forward_impl& operator=(const perfect_forward_impl&) = default; perfect_forward_impl& operator=(perfect_forward_impl&&) = default; - template >> + template > > constexpr auto operator()(Args&&... args) & noexcept(noexcept(Op()(std::get(bound_args_)..., std::forward(args)...))) -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { return Op()(std::get(bound_args_)..., std::forward(args)...); } - template >> + template > > auto operator()(Args&&...) & = delete; - template >> + template > > constexpr auto operator()(Args&&... args) const& noexcept(noexcept(Op()(std::get(bound_args_)..., std::forward(args)...))) -> decltype(Op()(std::get(bound_args_)..., std::forward(args)...)) { return Op()(std::get(bound_args_)..., std::forward(args)...); } - template >> + template > > auto operator()(Args&&...) const& = delete; - template >> + template > > constexpr auto operator()(Args&&... args) && noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., std::forward(args)...))) -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); } - template >> + template > > auto operator()(Args&&...) && = delete; - template >> + template > > constexpr auto operator()(Args&&... args) const&& noexcept(noexcept(Op()(std::get(std::move(bound_args_))..., std::forward(args)...))) -> decltype(Op()(std::get(std::move(bound_args_))..., std::forward(args)...)) { return Op()(std::get(std::move(bound_args_))..., std::forward(args)...); } - template >> + template > > auto operator()(Args&&...) const&& = delete; }; @@ -178,9 +178,9 @@ struct compose_t : perfect_forward { template constexpr auto compose(Fn1&& f1, Fn2&& f2) noexcept( - noexcept(compose_t, std::decay_t>(std::forward(f1), std::forward(f2)))) - -> decltype(compose_t, std::decay_t>(std::forward(f1), std::forward(f2))) { - return compose_t, std::decay_t>(std::forward(f1), std::forward(f2)); + noexcept(compose_t, std::decay_t >(std::forward(f1), std::forward(f2)))) + -> decltype(compose_t, std::decay_t >(std::forward(f1), std::forward(f2))) { + return compose_t, std::decay_t >(std::forward(f1), std::forward(f2)); } // CRTP base that one can derive from in order to be considered a range adaptor closure @@ -194,25 +194,25 @@ struct range_adaptor_closure; // Type that wraps an arbitrary function object and makes it into a range adaptor closure, // i.e. something that can be called via the `x | f` notation. template -struct range_adaptor_closure_t : Fn, range_adaptor_closure> { +struct range_adaptor_closure_t : Fn, range_adaptor_closure > { constexpr explicit range_adaptor_closure_t(Fn&& f) : Fn(std::move(f)) {} }; template concept RangeAdaptorClosure = - std::derived_from, range_adaptor_closure>>; + std::derived_from, range_adaptor_closure > >; template struct range_adaptor_closure { template - requires std::same_as> && std::invocable + requires std::same_as > && std::invocable [[nodiscard]] friend constexpr decltype(auto) operator|(View&& view, Closure&& closure) noexcept(std::is_nothrow_invocable_v) { return std::invoke(std::forward(closure), std::forward(view)); } template - requires std::same_as> && + requires std::same_as > && std::constructible_from, Closure> && std::constructible_from, OtherClosure> [[nodiscard]] friend constexpr auto @@ -223,11 +223,11 @@ struct range_adaptor_closure { } }; -template > +template > struct bind_back_op; template -struct bind_back_op> { +struct bind_back_op > { template constexpr auto operator()(Fn&& f, BoundArgs&& bound_args, Args&&... args) const noexcept(noexcept(std::invoke( std::forward(f), std::forward(args)..., std::get(std::forward(bound_args))...))) @@ -240,20 +240,20 @@ struct bind_back_op> { }; template -struct bind_back_t : perfect_forward>, Fn, BoundArgs> { - using perfect_forward>, Fn, BoundArgs>::perfect_forward; +struct bind_back_t : perfect_forward >, Fn, BoundArgs> { + using perfect_forward >, Fn, BoundArgs>::perfect_forward; }; template - requires std::is_constructible_v, Fn> && std::is_move_constructible_v> && + requires std::is_constructible_v, Fn> && std::is_move_constructible_v > && (std::is_constructible_v, Args> && ...) && - (std::is_move_constructible_v> && ...) + (std::is_move_constructible_v > && ...) constexpr auto -bind_back(Fn&& f, Args&&... args) noexcept(noexcept(bind_back_t, std::tuple...>>( +bind_back(Fn&& f, Args&&... args) noexcept(noexcept(bind_back_t, std::tuple...> >( std::forward(f), std::forward_as_tuple(std::forward(args)...)))) - -> decltype(bind_back_t, std::tuple...>>( + -> decltype(bind_back_t, std::tuple...> >( std::forward(f), std::forward_as_tuple(std::forward(args)...))) { - return bind_back_t, std::tuple...>>( + return bind_back_t, std::tuple...> >( std::forward(f), std::forward_as_tuple(std::forward(args)...)); } @@ -267,22 +267,22 @@ enum class scan_view_kind : bool { unseeded, seeded }; template concept scannable_impl = // exposition only - std::movable && std::convertible_to && std::invocable> && - std::assignable_from>>; + std::movable && std::convertible_to && std::invocable > && + std::assignable_from > >; template concept scannable = // exposition only - std::invocable> && - std::convertible_to>, - std::decay_t>>> && - scannable_impl>>>; + std::invocable > && + std::convertible_to >, + std::decay_t > > > && + scannable_impl > > >; template requires std::ranges::view && std::is_object_v && std::is_object_v && scannable -class scan_view : public std::ranges::view_interface> { +class scan_view : public std::ranges::view_interface > { private: // [range.scan.iterator], class template scan_view::iterator template @@ -333,7 +333,7 @@ class scan_view : public std::ranges::view_interface> { return std::ranges::size(base_); } -#if __cpp_lib_ranges_reserve_hint >= 202502L + #if __cpp_lib_ranges_reserve_hint >= 202502L constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { @@ -345,11 +345,11 @@ class scan_view : public std::ranges::view_interface> { { return std::ranges::reserve_hint(base_); } -#endif + #endif }; template -scan_view(R&&, F) -> scan_view, F, std::ranges::range_value_t>; +scan_view(R&&, F) -> scan_view, F, std::ranges::range_value_t >; template scan_view(R&&, F, T) -> scan_view, F, T, scan_view_kind::seeded>; @@ -362,7 +362,7 @@ class scan_view::iterator { using Base = detail::maybe_const; // exposition only using Func = detail::maybe_const; // exposition only using ResultType = std::decay_t< // exposition only - std::invoke_result_t>>; + std::invoke_result_t > >; struct Holder { // exposition only std::ranges::sentinel_t end_ = std::ranges::sentinel_t(); // exposition only @@ -406,7 +406,7 @@ class scan_view::iterator { using difference_type = std::ranges::range_difference_t; iterator() - requires std::default_initializable> + requires std::default_initializable > = default; constexpr iterator(Parent& parent, std::ranges::iterator_t current) : current_{std::move(current)}, parent_{init(parent)} { @@ -419,7 +419,7 @@ class scan_view::iterator { } } constexpr iterator(iterator i) - requires Const && std::convertible_to, std::ranges::iterator_t> + requires Const && std::convertible_to, std::ranges::iterator_t > : current_{std::move(i.current_)}, parent_{std::move(i.parent_)}, sum_{std::move(i.sum_)} {} constexpr const std::ranges::iterator_t& base() const& noexcept { return current_; } @@ -436,7 +436,7 @@ class scan_view::iterator { constexpr void operator++(int) { ++*this; } friend constexpr bool operator==(const iterator& x, const iterator& y) - requires std::equality_comparable> + requires std::equality_comparable > { return x.current_ == y.current_; } From 67d2eb475a707fbf30b8de41d6ee3853292c810b Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Tue, 12 May 2026 19:34:47 -0400 Subject: [PATCH 24/26] Put #include to GMF --- include/beman/scan_view/scan_view.cppm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/beman/scan_view/scan_view.cppm b/include/beman/scan_view/scan_view.cppm index 4cb6fd3..e2bfa71 100644 --- a/include/beman/scan_view/scan_view.cppm +++ b/include/beman/scan_view/scan_view.cppm @@ -1,11 +1,13 @@ +module; + +#include + export module beman.scan_view; import std; using size_t = std::size_t; -#define __cpp_lib_ranges 202207L - #define BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT export { #pragma clang diagnostic push From ec3b6219fedfc1996213248758e68d71a0267cf0 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Tue, 12 May 2026 19:38:17 -0400 Subject: [PATCH 25/26] Replace size_t with std::size_t --- include/beman/scan_view/scan.hpp | 6 +++--- include/beman/scan_view/scan_view.cppm | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/beman/scan_view/scan.hpp b/include/beman/scan_view/scan.hpp index e5ac628..369e32a 100644 --- a/include/beman/scan_view/scan.hpp +++ b/include/beman/scan_view/scan.hpp @@ -100,7 +100,7 @@ using maybe_const = std::conditional_t; template struct perfect_forward_impl; -template +template struct perfect_forward_impl, BoundArgs...> { private: std::tuple bound_args_; @@ -223,10 +223,10 @@ struct range_adaptor_closure { } }; -template > +template > struct bind_back_op; -template +template struct bind_back_op > { template constexpr auto operator()(Fn&& f, BoundArgs&& bound_args, Args&&... args) const noexcept(noexcept(std::invoke( diff --git a/include/beman/scan_view/scan_view.cppm b/include/beman/scan_view/scan_view.cppm index e2bfa71..0f4bc6e 100644 --- a/include/beman/scan_view/scan_view.cppm +++ b/include/beman/scan_view/scan_view.cppm @@ -6,8 +6,6 @@ export module beman.scan_view; import std; -using size_t = std::size_t; - #define BEMAN_SCAN_VIEW_INCLUDED_FROM_INTERFACE_UNIT export { #pragma clang diagnostic push From b65932f5ba08adb76f346a6eb976d63009cedff3 Mon Sep 17 00:00:00 2001 From: Alex Yesmanchyk Date: Tue, 12 May 2026 19:40:08 -0400 Subject: [PATCH 26/26] Single #include --- tests/beman/scan_view/basic.test.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/beman/scan_view/basic.test.cpp b/tests/beman/scan_view/basic.test.cpp index bec23bd..aee8ab3 100644 --- a/tests/beman/scan_view/basic.test.cpp +++ b/tests/beman/scan_view/basic.test.cpp @@ -2,9 +2,9 @@ #include -#if BEMAN_SCAN_VIEW_USE_MODULES() +#include - #include +#if BEMAN_SCAN_VIEW_USE_MODULES() import std; @@ -15,8 +15,6 @@ import std; #include #include - #include - #endif #include