From 42361a924d924daa315866df2ebbc7ea69727125 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:57:28 +0900 Subject: [PATCH] Improve `run_length_sequence` interface --- include/iris/error/throwf.hpp | 5 +- include/iris/error/throwf_format.hpp | 8 +-- include/iris/run_length_sequence.hpp | 100 +++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/include/iris/error/throwf.hpp b/include/iris/error/throwf.hpp index eac5e0c..7eb18d3 100644 --- a/include/iris/error/throwf.hpp +++ b/include/iris/error/throwf.hpp @@ -24,11 +24,12 @@ inline namespace error_functions { // critical paths as some compilers fail to optimize the plain `throw` statement // even though the statement itself should imply `[[noreturn]]`. template + requires std::is_default_constructible_v IRIS_CONFIG_THROW_NORETURN void throwf() { static_assert(std::is_base_of_v); static_assert(std::is_constructible_v); - IRIS_CONFIG_THROW_IMPL(E{}); + IRIS_CONFIG_THROW_IMPL(E()); } // This function can be used to strongly assume optimization in some performance- @@ -40,7 +41,7 @@ IRIS_CONFIG_THROW_NORETURN void throwf(Arg&& arg, Rest&&... rest) { static_assert(std::is_base_of_v); static_assert(!std::is_base_of_v>, "don't copy/move construct exception types directly"); - IRIS_CONFIG_THROW_IMPL(E{std::forward(arg), std::forward(rest)...}); + IRIS_CONFIG_THROW_IMPL(E(std::forward(arg), std::forward(rest)...)); } } // error_functions diff --git a/include/iris/error/throwf_format.hpp b/include/iris/error/throwf_format.hpp index 0403227..2235833 100644 --- a/include/iris/error/throwf_format.hpp +++ b/include/iris/error/throwf_format.hpp @@ -33,7 +33,7 @@ template IRIS_CONFIG_THROW_NORETURN void throwf(std::format_string fmt, Args&&... args) { static_assert(std::is_base_of_v); - IRIS_CONFIG_THROW_IMPL(E{std::format(std::move(fmt), std::forward(args)...)}); + IRIS_CONFIG_THROW_IMPL(E(std::format(std::move(fmt), std::forward(args)...))); } template @@ -41,7 +41,7 @@ template IRIS_CONFIG_THROW_NORETURN void throwf(Arg0&& arg0, std::format_string fmt, Args&&... args) { static_assert(std::is_base_of_v); - IRIS_CONFIG_THROW_IMPL(E{std::forward(arg0), std::format(std::move(fmt), std::forward(args)...)}); + IRIS_CONFIG_THROW_IMPL(E(std::forward(arg0), std::format(std::move(fmt), std::forward(args)...))); } template @@ -50,10 +50,10 @@ IRIS_CONFIG_THROW_NORETURN void throwf(Arg0&& arg0, Arg1&& arg1, std::format_str { static_assert(std::is_base_of_v); IRIS_CONFIG_THROW_IMPL( - E{ + E( std::forward(arg0), std::forward(arg1), std::format(std::move(fmt), std::forward(args)...) - } + ) ); } diff --git a/include/iris/run_length_sequence.hpp b/include/iris/run_length_sequence.hpp index 3ee71c2..4eb3be1 100644 --- a/include/iris/run_length_sequence.hpp +++ b/include/iris/run_length_sequence.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -145,6 +146,23 @@ class run_length_sequence return {index(), *value_it_}; } + [[nodiscard]] constexpr T const& run_value() const noexcept + { + return *value_it_; + } + + constexpr void advance_run() noexcept + { + ++ofs_it_; + ++value_it_; + rel_pos_ = static_cast(0u); + } + [[nodiscard]] friend constexpr iterator_impl next_run(iterator_impl it) noexcept + { + it.advance_run(); + return it; + } + constexpr iterator_impl& operator++() noexcept { auto ofs_next = std::next(ofs_it_); @@ -469,6 +487,88 @@ class run_length_sequence ); } + template + requires std::predicate + [[nodiscard]] constexpr const_iterator find_run_if(const_iterator from, Pred&& pred) const + noexcept(std::is_nothrow_invocable_v) + { + auto const last = this->end(); + for (; from != last; from.advance_run()) { + if (std::invoke(pred, from.run_value())) { + return from; + } + } + return last; + } + + template + requires std::predicate + [[nodiscard]] constexpr const_iterator find_run_if_not(const_iterator from, Pred&& pred) const + noexcept(std::is_nothrow_invocable_v) + { + auto const last = this->end(); + for (; from != last; from.advance_run()) { + if (!std::invoke(pred, from.run_value())) { + return from; + } + } + return last; + } + + template + requires req::half_equality_comparable + [[nodiscard]] constexpr const_iterator find_run(const_iterator from, U const& value) const + noexcept(noexcept(std::declval() == value)) + { + return this->find_run_if(std::move(from), [&](T const& e) noexcept(noexcept(e == value)) { + return e == value; + }); + } + + template + requires req::half_equality_comparable + [[nodiscard]] constexpr const_iterator find_run_not(const_iterator from, U const& value) const + noexcept(noexcept(std::declval() != value)) + { + return this->find_run_if(std::move(from), [&](T const& e) noexcept(noexcept(e != value)) { + return e != value; + }); + } + + // --------------------------------------------------------------------- + + template + requires std::predicate + [[nodiscard]] constexpr const_iterator find_run_if(Pred&& pred) const + noexcept(noexcept(this->find_run_if(begin(), std::forward(pred)))) + { + return this->find_run_if(begin(), std::forward(pred)); + } + + template + requires std::predicate + [[nodiscard]] constexpr const_iterator find_run_if_not(Pred&& pred) const + noexcept(noexcept(this->find_run_if_not(begin(), std::forward(pred)))) + { + return this->find_run_if_not(begin(), std::forward(pred)); + } + + template + requires req::half_equality_comparable + [[nodiscard]] constexpr const_iterator find_run(U const& value) const + noexcept(noexcept(this->find_run(begin(), value))) + { + return this->find_run(begin(), value); + } + + template + requires req::half_equality_comparable + [[nodiscard]] constexpr const_iterator find_run_not(U const& value) const + noexcept(noexcept(this->find_run_not(begin(), value))) + { + return this->find_run_not(begin(), value); + } + // --------------------------------------------------------------------- // Returns an iterator to the element at logical position `pos`, or `end()`