Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion module/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
153 changes: 122 additions & 31 deletions module/common/include/oops/view.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#pragma once

#include <cassert>
#include <cstdint>
#include <iterator>
#include <limits>
#include <utility>

namespace oops {
namespace view { // C++标准库设计语境下,view是非持有、惰性求值的轻量级range
template <typename T>
class ReverseView {
public:
Expand Down Expand Up @@ -34,51 +37,139 @@ class ReverseView {

template <typename T>
auto Reverse(T &&t) {
return ReverseView<T>{std::forward<T>(t)};
return ReverseView<T>(std::forward<T>(t));
}

struct UnreachableSentinel {
template <typename T>
bool operator==(const T &t) const noexcept {
return false;
}
template <typename T>
struct ArithmeticOverflowResult {
constexpr explicit operator bool() const noexcept { return overflow; }
T value{};
bool overflow{};
};

template <typename T, typename Bound = UnreachableSentinel>
class IotaView {
template <typename T>
[[nodiscard]] constexpr auto AddOverflowGeneric(T a, T b) noexcept {
static_assert(std::is_integral_v<T>);
using UnsignedT = std::make_unsigned_t<T>;

ArithmeticOverflowResult<T> res;
res.value = static_cast<T>(static_cast<UnsignedT>(a) + static_cast<UnsignedT>(b));

if constexpr (std::is_unsigned_v<T>) {
res.overflow = res.value < a;
} else {
res.overflow = ((a ^ b) >= 0) && ((a ^ res.value) < 0);
}
return res;
}

template <typename T>
[[nodiscard]] constexpr auto AddOverflow(T a, T b) noexcept {
#if defined(__GNUC__) || defined(__clang__)
ArithmeticOverflowResult<T> res;
res.overflow = __builtin_add_overflow(a, b, &res.value);
return res;
#else
return AddOverflowGeneric(a, b);
#endif
}

template <typename T, typename Stop = T>
class RangeView {
static_assert(std::is_integral_v<T>);
static_assert(std::is_same_v<T, Stop> || !std::is_integral_v<Stop>);

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 <typename T, typename Stop>
class RangeView<T, Stop>::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<Stop>) {
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 <typename T, typename Bound>
constexpr auto Iota(T &&t, Bound &&bound) {
return IotaView<T, Bound>(t, bound);
template <typename T, typename Stop>
constexpr auto Range(T start, Stop stop, T step = 1) {
if constexpr (std::is_integral_v<Stop>) {
return RangeView<T>(start, stop, step);
} else {
return RangeView<T, Stop>(start, stop, step);
}
}

template <typename T, typename Stop, std::enable_if_t<!std::is_integral_v<Stop>, int> = 0>
constexpr auto Range(Stop stop) {
return RangeView<T, Stop>(stop);
}

template <typename T>
constexpr auto Iota(T &&t) {
return IotaView<T>(t);
constexpr auto Range(T stop) {
return RangeView<T>(stop);
}

struct RangeOverflow {
template <typename Iter>
constexpr bool operator==(const Iter &iter) const noexcept {
return iter.Overflow();
}
};
constexpr RangeOverflow RANGE_OVERFLOW;

template <typename T>
constexpr auto IntegerSet{Range(std::numeric_limits<T>::min(), RANGE_OVERFLOW)};
} // namespace view
} // namespace oops
8 changes: 4 additions & 4 deletions module/common/src/str.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
17 changes: 7 additions & 10 deletions module/common/test/test_str.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
#include "oops/view.h"
#include "gtest/gtest.h"

auto CHAR_SET{
oops::Iota<int>(std::numeric_limits<char>::min(), static_cast<short>(std::numeric_limits<char>::max()) + 1)};

template <auto Value>
constexpr auto CONSTEXPR_VALUE{Value};

Expand All @@ -31,7 +28,7 @@ TEST(CommonStr, IsUpper) {
}

TEST(CommonStr, IsUpperSameAsStd) {
for (char c : CHAR_SET) {
for (char c : oops::view::IntegerSet<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::IsUpper(c), std::isupper(c_int) != 0)
Expand All @@ -56,7 +53,7 @@ TEST(CommonStr, IsLower) {
}

TEST(CommonStr, IsLowerSameAsStd) {
for (char c : CHAR_SET) {
for (char c : oops::view::IntegerSet<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::IsLower(c), std::islower(c_int) != 0)
Expand All @@ -83,7 +80,7 @@ TEST(CommonStr, IsAlpha) {
}

TEST(CommonStr, IsAlphaSameAsStd) {
for (char c : CHAR_SET) {
for (char c : oops::view::IntegerSet<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::IsAlpha(c), std::isalpha(c_int) != 0)
Expand All @@ -107,7 +104,7 @@ TEST(CommonStr, IsDigit) {
}

TEST(CommonStr, IsDigitSameAsStd) {
for (char c : CHAR_SET) {
for (char c : oops::view::IntegerSet<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::IsDigit(c), std::isdigit(c_int) != 0)
Expand Down Expand Up @@ -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<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::IsAlnum(c), std::isalnum(c_int) != 0)
Expand All @@ -165,7 +162,7 @@ TEST(CommonStr, ToLowerConstexpr) {
}

TEST(CommonStr, ToLowerSameAsStd) {
for (char c : CHAR_SET) {
for (char c : oops::view::IntegerSet<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::ToLower(c), static_cast<char>(std::tolower(c_int)))
Expand All @@ -190,7 +187,7 @@ TEST(CommonStr, ToUpper) {
}

TEST(CommonStr, ToUpperSameAsStd) {
for (char c : CHAR_SET) {
for (char c : oops::view::IntegerSet<char>) {
int c_int{static_cast<unsigned char>(c)};
// TODO(resserops): 后续16进制通过formatter控制
EXPECT_EQ(oops::ToUpper(c), static_cast<char>(std::toupper(c_int)))
Expand Down
Loading
Loading