From 044bcc9dafe544e8511ba79d7cb80ae2902c44cb Mon Sep 17 00:00:00 2001 From: resserops Date: Tue, 21 Apr 2026 01:02:25 +0800 Subject: [PATCH] refactor(common/view): replace IotaView with RangeView --- build.py | 6 +- module/common/CMakeLists.txt | 2 +- module/common/include/oops/view.h | 153 +++++++++++++++++----- module/common/src/str.cpp | 8 +- module/common/test/test_str.cpp | 17 +-- module/common/test/test_view.cpp | 102 +++++++++++---- module/profiling/include/oops/cpu_timer.h | 4 +- 7 files changed, 219 insertions(+), 73 deletions(-) diff --git a/build.py b/build.py index 9620ff9..61bb72c 100755 --- a/build.py +++ b/build.py @@ -18,11 +18,11 @@ cmake_args += f'-DENABLE_TEST={args.test} ' def system_errexit(command: str): - ret = os.system(command) - exit_code = ret >> 8 + res = os.system(command) + exit_code = res >> 8 if exit_code != 0: sys.exit(exit_code) - signal = ret & 0xFF + signal = res & 0xFF if signal != 0: sys.exit(128 + signal) diff --git a/module/common/CMakeLists.txt b/module/common/CMakeLists.txt index 4036ca7..ed5c9a2 100644 --- a/module/common/CMakeLists.txt +++ b/module/common/CMakeLists.txt @@ -24,7 +24,7 @@ if(ENABLE_TEST) # 构建测试对象库 file(GLOB_RECURSE TEST_SRC "test/*.cpp") add_library(oops_common_test_o OBJECT ${TEST_SRC}) - target_link_libraries(oops_common_test_o PRIVATE oops_common_i gtest) + target_link_libraries(oops_common_test_o PRIVATE oops_common_i gtest gmock) # 构建模块测试程序 add_executable(oops_common_test) diff --git a/module/common/include/oops/view.h b/module/common/include/oops/view.h index 4a5e776..d64b793 100644 --- a/module/common/include/oops/view.h +++ b/module/common/include/oops/view.h @@ -1,10 +1,13 @@ #pragma once +#include +#include #include #include #include namespace oops { +namespace view { // C++标准库设计语境下,view是非持有、惰性求值的轻量级range template class ReverseView { public: @@ -34,51 +37,139 @@ class ReverseView { template auto Reverse(T &&t) { - return ReverseView{std::forward(t)}; + return ReverseView(std::forward(t)); } -struct UnreachableSentinel { - template - bool operator==(const T &t) const noexcept { - return false; - } +template +struct ArithmeticOverflowResult { + constexpr explicit operator bool() const noexcept { return overflow; } + T value{}; + bool overflow{}; }; -template -class IotaView { +template +[[nodiscard]] constexpr auto AddOverflowGeneric(T a, T b) noexcept { + static_assert(std::is_integral_v); + using UnsignedT = std::make_unsigned_t; + + ArithmeticOverflowResult res; + res.value = static_cast(static_cast(a) + static_cast(b)); + + if constexpr (std::is_unsigned_v) { + res.overflow = res.value < a; + } else { + res.overflow = ((a ^ b) >= 0) && ((a ^ res.value) < 0); + } + return res; +} + +template +[[nodiscard]] constexpr auto AddOverflow(T a, T b) noexcept { +#if defined(__GNUC__) || defined(__clang__) + ArithmeticOverflowResult res; + res.overflow = __builtin_add_overflow(a, b, &res.value); + return res; +#else + return AddOverflowGeneric(a, b); +#endif +} + +template +class RangeView { + static_assert(std::is_integral_v); + static_assert(std::is_same_v || !std::is_integral_v); + public: - class Iter { - public: - constexpr explicit Iter(T t) : t_{t} {} - constexpr T operator*() const { return t_; } - constexpr bool operator!=(const Bound &bound) const { return t_ != bound; } - constexpr Iter &operator++() { - ++t_; - return *this; + class Iterator; + constexpr RangeView(Stop stop) : stop_{stop} {} + constexpr RangeView(T start, Stop stop, T step = 1) : start_{start}, stop_{stop}, step_{step} { + if (step == 0) { + throw std::invalid_argument("range() arg 3 must not be zero"); } + } + + constexpr Iterator begin() const { return Iterator(start_, step_); } + constexpr Stop end() const { return stop_; } + +private: + T start_{0}; + Stop stop_; + T step_{1}; +}; - private: - T t_; - }; +template +class RangeView::Iterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = const T *; + using reference = const T &; + + constexpr explicit Iterator(T t, T step) : t_{t}, step_{step} {} + constexpr T operator*() const noexcept { return t_; } + + constexpr Iterator &operator++() noexcept { + auto res{AddOverflow(t_, step_)}; + t_ = res.value; + overflow_ = res.overflow; + return *this; + } - constexpr explicit IotaView(T t) : t_{t} {} - constexpr IotaView(T t, Bound bound) : t_{t}, bound_{bound} {} + constexpr Iterator operator++(int) noexcept { + auto iter{*this}; + ++(*this); + return iter; + } - constexpr Iter begin() const { return Iter(t_); } - constexpr Bound end() const { return bound_; } + bool Overflow() const { return overflow_; } + T Step() const { return step_; } + + constexpr bool operator==(Stop stop) const { + if constexpr (std::is_integral_v) { + if (step_ > 0) { + return stop <= t_; + } + return stop >= t_; + } else { + return stop == (*this); + } + } + constexpr bool operator!=(Stop stop) const { return !operator==(stop); } -private: - T t_; - Bound bound_; +protected: + T t_, step_; + bool overflow_{false}; }; -template -constexpr auto Iota(T &&t, Bound &&bound) { - return IotaView(t, bound); +template +constexpr auto Range(T start, Stop stop, T step = 1) { + if constexpr (std::is_integral_v) { + return RangeView(start, stop, step); + } else { + return RangeView(start, stop, step); + } +} + +template , int> = 0> +constexpr auto Range(Stop stop) { + return RangeView(stop); } template -constexpr auto Iota(T &&t) { - return IotaView(t); +constexpr auto Range(T stop) { + return RangeView(stop); } + +struct RangeOverflow { + template + constexpr bool operator==(const Iter &iter) const noexcept { + return iter.Overflow(); + } +}; +constexpr RangeOverflow RANGE_OVERFLOW; + +template +constexpr auto IntegerSet{Range(std::numeric_limits::min(), RANGE_OVERFLOW)}; +} // namespace view } // namespace oops diff --git a/module/common/src/str.cpp b/module/common/src/str.cpp index 426fa8d..3138f70 100644 --- a/module/common/src/str.cpp +++ b/module/common/src/str.cpp @@ -8,12 +8,12 @@ std::string Repeat(const std::string &str, std::size_t n) { if (n == 0 || str.empty()) { return {}; } - std::string ret; - ret.reserve(n * str.size()); + std::string res; + res.reserve(n * str.size()); for (std::size_t i{0}; i < n; ++i) { - ret.append(str); + res.append(str); } - return ret; + return res; } std::string SplitBack(const std::string &str, const std::string &delim) { diff --git a/module/common/test/test_str.cpp b/module/common/test/test_str.cpp index df36295..f17cff0 100644 --- a/module/common/test/test_str.cpp +++ b/module/common/test/test_str.cpp @@ -4,9 +4,6 @@ #include "oops/view.h" #include "gtest/gtest.h" -auto CHAR_SET{ - oops::Iota(std::numeric_limits::min(), static_cast(std::numeric_limits::max()) + 1)}; - template constexpr auto CONSTEXPR_VALUE{Value}; @@ -31,7 +28,7 @@ TEST(CommonStr, IsUpper) { } TEST(CommonStr, IsUpperSameAsStd) { - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::IsUpper(c), std::isupper(c_int) != 0) @@ -56,7 +53,7 @@ TEST(CommonStr, IsLower) { } TEST(CommonStr, IsLowerSameAsStd) { - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::IsLower(c), std::islower(c_int) != 0) @@ -83,7 +80,7 @@ TEST(CommonStr, IsAlpha) { } TEST(CommonStr, IsAlphaSameAsStd) { - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::IsAlpha(c), std::isalpha(c_int) != 0) @@ -107,7 +104,7 @@ TEST(CommonStr, IsDigit) { } TEST(CommonStr, IsDigitSameAsStd) { - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::IsDigit(c), std::isdigit(c_int) != 0) @@ -138,7 +135,7 @@ TEST(CommonStr, IsAlnum) { TEST(CommonStr, IsAlnumSameAsStd) { std::size_t count{0}; - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::IsAlnum(c), std::isalnum(c_int) != 0) @@ -165,7 +162,7 @@ TEST(CommonStr, ToLowerConstexpr) { } TEST(CommonStr, ToLowerSameAsStd) { - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::ToLower(c), static_cast(std::tolower(c_int))) @@ -190,7 +187,7 @@ TEST(CommonStr, ToUpper) { } TEST(CommonStr, ToUpperSameAsStd) { - for (char c : CHAR_SET) { + for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; // TODO(resserops): 后续16进制通过formatter控制 EXPECT_EQ(oops::ToUpper(c), static_cast(std::toupper(c_int))) diff --git a/module/common/test/test_view.cpp b/module/common/test/test_view.cpp index 4017e9f..dca8633 100644 --- a/module/common/test/test_view.cpp +++ b/module/common/test/test_view.cpp @@ -1,96 +1,96 @@ #include "oops/view.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" -using namespace oops; +using namespace oops::view; +using namespace testing; -#define EXPECT_IL_EQ(lhs, ...) EXPECT_EQ(lhs, (decltype(lhs){__VA_ARGS__})) - -TEST(CommonView, ReverseView) { +TEST(CommonView, Reverse) { std::vector vec{1, 2, 3, 4, 5}; std::vector res; for (int i : Reverse(vec)) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); res.clear(); for (const int &i : Reverse(vec)) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); res.clear(); for (int &i : Reverse(vec)) { res.push_back(i); i += 2; } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); - EXPECT_IL_EQ(vec, 3, 4, 5, 6, 7); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); + EXPECT_THAT(vec, ElementsAre(3, 4, 5, 6, 7)); auto r_view{Reverse(vec)}; EXPECT_EQ(sizeof(decltype(r_view)), sizeof(void *)); } -TEST(CommonView, ReverseViewConst) { +TEST(CommonView, ReverseConst) { const std::vector vec{1, 2, 3, 4, 5}; std::vector res; for (int i : Reverse(vec)) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); res.clear(); for (const int &i : Reverse(vec)) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); auto r_view{Reverse(vec)}; EXPECT_EQ(sizeof(decltype(r_view)), sizeof(void *)); } -TEST(CommonView, ReverseViewRvalue) { +TEST(CommonView, ReverseRvalue) { std::vector res; for (int i : Reverse(std::vector{1, 2, 3, 4, 5})) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); res.clear(); for (const int &i : Reverse(std::vector{1, 2, 3, 4, 5})) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); res.clear(); for (int &i : Reverse(std::vector{1, 2, 3, 4, 5})) { res.push_back(i); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); auto r_view{Reverse(std::vector{})}; EXPECT_EQ(sizeof(decltype(r_view)), sizeof(std::vector)); } -TEST(CommonView, ReverseViewNest) { +TEST(CommonView, ReverseNest) { std::vector vec{1, 2, 3, 4, 5}; std::vector res; for (int &i : Reverse(Reverse(vec))) { res.push_back(i); i += 2; } - EXPECT_IL_EQ(res, 1, 2, 3, 4, 5); - EXPECT_IL_EQ(vec, 3, 4, 5, 6, 7); + EXPECT_THAT(res, ElementsAre(1, 2, 3, 4, 5)); + EXPECT_THAT(vec, ElementsAre(3, 4, 5, 6, 7)); const std::vector const_vec{1, 2, 3, 4, 5}; res.clear(); for (const int &i : Reverse(Reverse(const_vec))) { res.push_back(i); } - EXPECT_IL_EQ(res, 1, 2, 3, 4, 5); + EXPECT_THAT(res, ElementsAre(1, 2, 3, 4, 5)); } -TEST(CommonView, ReverseViewNestConstIter) { +TEST(CommonView, ReverseNestConstIter) { std::vector vec{1, 2, 3, 4, 5}; std::vector res; @@ -99,11 +99,69 @@ TEST(CommonView, ReverseViewNestConstIter) { for (auto iter{r_view.cbegin()}; iter != r_view.cend(); ++iter) { res.push_back(*iter); } - EXPECT_IL_EQ(res, 5, 4, 3, 2, 1); + EXPECT_THAT(res, ElementsAre(5, 4, 3, 2, 1)); res.clear(); for (auto iter{r_view.crbegin()}; iter != r_view.crend(); ++iter) { res.push_back(*iter); } - EXPECT_IL_EQ(res, 1, 2, 3, 4, 5); + EXPECT_THAT(res, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(CommonView, Range) { + std::vector res; + for (auto i : oops::view::Range(10)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + + res.clear(); + for (auto i : oops::view::Range(1, 11)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + + res.clear(); + for (auto i : oops::view::Range(0, 30, 5)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre(0, 5, 10, 15, 20, 25)); + + res.clear(); + for (auto i : oops::view::Range(0, 10, 3)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre(0, 3, 6, 9)); + + res.clear(); + for (auto i : oops::view::Range(0, -10, -1)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre(0, -1, -2, -3, -4, -5, -6, -7, -8, -9)); + + res.clear(); + for (auto i : oops::view::Range(0)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre()); + + res.clear(); + for (auto i : oops::view::Range(1, 0)) { + res.push_back(i); + } + EXPECT_THAT(res, ElementsAre()); +} + +TEST(CommonView, IntegerSet) { + int count{0}; + for ([[maybe_unused]] auto i : IntegerSet) { + ++count; + } + oops::view::Range((short)2, RANGE_OVERFLOW); + oops::view::Range(2, 21); + oops::view::Range(21); + oops::view::Range(RANGE_OVERFLOW); + oops::view::Range(10); + // oops::view::Range(RANGE_OVERFLOW); + EXPECT_EQ(count, 256); } diff --git a/module/profiling/include/oops/cpu_timer.h b/module/profiling/include/oops/cpu_timer.h index b8dfa43..35be17e 100644 --- a/module/profiling/include/oops/cpu_timer.h +++ b/module/profiling/include/oops/cpu_timer.h @@ -84,9 +84,9 @@ class CpuTimer { return std::monostate{}; // 无法打开文件,返回空类型 } - auto ret{std::fgets(buf, sizeof(buf), f)}; + auto res{std::fgets(buf, sizeof(buf), f)}; std::fclose(f); - if (ret == nullptr) { + if (res == nullptr) { return std::monostate{}; // 读取失败,返回空类型 }