From 62272807dcebbec96c2fbabc172c36e0eccaeae3 Mon Sep 17 00:00:00 2001 From: janis Date: Thu, 23 Jun 2022 17:26:19 +0100 Subject: [PATCH] initial commit --- .gitignore | 3 + Alloy.toml | 16 ++++ include/assert.hpp | 120 ++++++++++++++++++++++++ include/concepts/container.hpp | 41 +++++++++ include/concepts/traits.hpp | 79 ++++++++++++++++ include/enum.hpp | 158 +++++++++++++++++++++++++++++++ include/format.hpp | 50 ++++++++++ include/iterator.hpp | 35 +++++++ include/math.hpp | 16 ++++ include/move.hpp | 12 +++ include/option.hpp | 13 +++ include/print.hpp | 24 +++++ include/ranges/collect.hpp | 83 +++++++++++++++++ include/ranges/drop_last.hpp | 68 ++++++++++++++ include/ranges/enumerate.hpp | 91 ++++++++++++++++++ include/ranges/first.hpp | 42 +++++++++ include/ranges/generated.hpp | 82 +++++++++++++++++ include/result.hpp | 10 ++ include/source_location.hpp | 38 ++++++++ include/step_function.hpp | 101 ++++++++++++++++++++ include/string.hpp | 151 ++++++++++++++++++++++++++++++ include/tagged_union.hpp | 124 +++++++++++++++++++++++++ include/thread.hpp | 164 +++++++++++++++++++++++++++++++++ include/time.hpp | 74 +++++++++++++++ include/type_traits.hpp | 78 ++++++++++++++++ include/types.hpp | 24 +++++ src/main.cc | 50 ++++++++++ 27 files changed, 1747 insertions(+) create mode 100644 .gitignore create mode 100644 Alloy.toml create mode 100644 include/assert.hpp create mode 100644 include/concepts/container.hpp create mode 100644 include/concepts/traits.hpp create mode 100644 include/enum.hpp create mode 100644 include/format.hpp create mode 100644 include/iterator.hpp create mode 100644 include/math.hpp create mode 100644 include/move.hpp create mode 100644 include/option.hpp create mode 100644 include/print.hpp create mode 100644 include/ranges/collect.hpp create mode 100644 include/ranges/drop_last.hpp create mode 100644 include/ranges/enumerate.hpp create mode 100644 include/ranges/first.hpp create mode 100644 include/ranges/generated.hpp create mode 100644 include/result.hpp create mode 100644 include/source_location.hpp create mode 100644 include/step_function.hpp create mode 100644 include/string.hpp create mode 100644 include/tagged_union.hpp create mode 100644 include/thread.hpp create mode 100644 include/time.hpp create mode 100644 include/type_traits.hpp create mode 100644 include/types.hpp create mode 100644 src/main.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcc32a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +compile_commands.json +.cache/ +target/ diff --git a/Alloy.toml b/Alloy.toml new file mode 100644 index 0000000..33db694 --- /dev/null +++ b/Alloy.toml @@ -0,0 +1,16 @@ +[package] +name = "utils" +authors = [] +standard = "c++2b" +version = "0.1.0" + +[lib] +cc-flags = ["-DNOMINMAX", "-D_HAS_CXX20=1"] +exclude-sources = ["src/main.cc"] +reparent-headers = true +name = "utils" + +[[bin]] +cc-flags = ["-DNOMINMAX", "-g", "-gcodeview"] +ld-flags = ["-g"] +name = "test" diff --git a/include/assert.hpp b/include/assert.hpp new file mode 100644 index 0000000..cd448fb --- /dev/null +++ b/include/assert.hpp @@ -0,0 +1,120 @@ +#pragma once + +#if defined(UTIL_ASSERT_FORMAT) +#include "print.hpp" +#endif + +#ifdef assert +#pragma push_macro("assert") +#undef assert +#define UTIL_ASSERT_UNDEF +#endif + +#include +#include "move.hpp" +#include "source_location.hpp" +#include "type_traits.hpp" + +namespace util { +template +struct is_convertible + : std::integral_constant< + bool, + (decltype(detail::test_returnable(0))::value && + decltype(detail::test_implicitly_convertible(0))::value) || + (is_void::value && is_void::value)> {}; + +template +inline constexpr bool is_convertible_v = is_convertible::value; + +template +concept boolean_testable_impl = is_convertible_v && requires { + static_cast(declval()); +}; + +template +concept boolean_testable = requires(T &&t) { + { !static_cast(t) } -> boolean_testable_impl; +}; + +template +concept weakly_equality_comparable_with = requires (const remove_reference_t& a, const remove_reference_t& b) { + {a == b} -> boolean_testable; + {a != b} -> boolean_testable; + {b == a} -> boolean_testable; + {b == a} -> boolean_testable; +}; + +template +concept equality_comparable = weakly_equality_comparable_with; + +template +concept equality_comparable_with = weakly_equality_comparable_with; + +template +inline constexpr auto assert(B&& b, const source_location& source = source_location::current()) ->void { + if (!b) [[unlikely]] { + + #if defined (UTIL_ASSERT_FORMAT) + if constexpr (is_formattable_v) { + print(std::format("Assertion failed: {}\n", b)); + print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); + } else { + print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); + } + #else + printf("Assertion failed in function %s at %s:%d,%d", source.function_name(), source.file_name(), source.line(), source.column()); + #endif + } +} + +template +requires (equality_comparable_with) +inline constexpr auto assert_eq(A&& rhs, B&& lhs, const source_location& source = source_location::current()) ->void { + if (rhs != lhs) [[unlikely]] { + #if defined (UTIL_ASSERT_FORMAT) + if constexpr (is_formattable_v && is_formattable_v) { + print(std::format("Assertion failed: {} != {}\n", rhs, lhs)); + print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); + } else { + print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); + } + #else + printf("Assertion failed in function %s at %s:%d,%d", source.function_name(), source.file_name(), source.line(), source.column()); + #endif + } +} + +template +inline constexpr auto assert_eq(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void { + assert_eq(FWD(rhs), FWD(lhs),source); +} + + +template +requires (equality_comparable_with) +inline constexpr auto assert_ne(A&& rhs, B&& lhs, const source_location& source = source_location::current()) ->void { + if (rhs == lhs) [[unlikely]] { + #if defined (UTIL_ASSERT_FORMAT) + if constexpr (is_formattable_v && is_formattable_v) { + print(std::format("Assertion failed: {} == {}\n", rhs, lhs)); + print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); + } else { + print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); + } + #else + printf("Assertion failed in function %s at %s:%d,%d", source.function_name(), source.file_name(), source.line(), source.column()); + #endif + } +} + +template +inline constexpr auto assert_ne(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void { + assert_ne(FWD(rhs), FWD(lhs), source); +} +} // namespace util + +#ifdef UTIL_ASSERT_UNDEF +#pragma pop_macro("assert") +#undef UTIL_ASSERT_UNDEF +#endif \ No newline at end of file diff --git a/include/concepts/container.hpp b/include/concepts/container.hpp new file mode 100644 index 0000000..b1bcddf --- /dev/null +++ b/include/concepts/container.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace util { +template +concept reservable = requires(C &c, typename C::size_type i) { + {c.reserve(i)}; +}; + +template +concept back_insertible = requires(C &c, + const std::ranges::range_value_t &t) { + { std::back_inserter(c) } -> std::same_as>; + {c.push_back(t)}; +}; + +template +concept front_insertible = requires(C &c, + const std::ranges::range_value_t &t) { + { std::front_inserter(c) } -> std::same_as>; + {c.push_front(t)}; +}; + +template +concept insertible = requires(C &c, std::ranges::iterator_t i, + const std::ranges::range_value_t &t) { + { std::inserter(c, i) } -> std::same_as>; + {c.insert(i, t)}; +}; +template +concept emplaceable = requires(C &c, Ts... t) { + {c.emplace(std::move(t...))}; +}; + +template +concept back_emplaceable = requires(C &c, Ts... t) { + {c.emplace_back(std::move(t...))}; +}; +} // namespace util \ No newline at end of file diff --git a/include/concepts/traits.hpp b/include/concepts/traits.hpp new file mode 100644 index 0000000..479e41f --- /dev/null +++ b/include/concepts/traits.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include + +namespace util { + +template +concept Number = std::integral || std::floating_point; + +template +requires std::equality_comparable_with +struct is_equal : public std::integral_constant { +}; + +template +inline constexpr auto is_equal_v = is_equal::value; + +template +requires std::three_way_comparable_with +struct cmp { + constexpr static std::partial_ordering ordering = T <=> U; +}; + +template inline constexpr auto cmp_v = cmp::ordering; + +template +concept Compare = is_equal_v, ordering>; + +template +concept Equal = is_equal_v; + +template +concept all_same_as = (std::same_as && ...); + +template +concept explicit_convertible_to = requires(const T ct, T t, const U &cu) { + {t = cu}; + { ct.operator U() } -> std::same_as; +} +|| std::same_as; + +template +concept explicit_convertible_to_any_of = + (explicit_convertible_to, To> || ...); + +template +struct is_explicit_convertible_to_any_of + : std::bool_constant> {}; + +template +concept explicit_convertible_to_integral = + std::integral || explicit_convertible_to_any_of < std::remove_cv_t, +bool, char, signed char, unsigned char, wchar_t, +#ifdef __cpp_char8_t + char8_t, +#endif // __cpp_char8_t + char16_t, char32_t, short, unsigned short, int, unsigned int, long, + unsigned long, long long, unsigned long long > ; + +template +concept explicit_convertible_to_floating_point = std::floating_point || + explicit_convertible_to_any_of; + +template +struct is_explicit_convertible + : std::bool_constant> {}; + +template +inline constexpr bool is_explicit_convertible_v = + is_explicit_convertible::value; + +template +concept with_return_type = requires(Fn fn, Args... args) { + { fn(args...) } -> std::same_as; +}; + +} // namespace util \ No newline at end of file diff --git a/include/enum.hpp b/include/enum.hpp new file mode 100644 index 0000000..e536ed8 --- /dev/null +++ b/include/enum.hpp @@ -0,0 +1,158 @@ +#pragma once + +#include +#include + +namespace util { +namespace bitflag_operators { + +template +concept enum_type = std::is_enum_v; + +template auto operator&(const E &rhs, const E &lhs) -> E { + return static_cast(underlying_type_cast(rhs) & underlying_type_cast(lhs)); +} + +template auto operator&=(E &rhs, const E &lhs) -> E & { + rhs = rhs & lhs; + return rhs; +} + +template auto operator|(const E &rhs, const E &lhs) -> E { + return static_cast(underlying_type_cast(rhs) | underlying_type_cast(lhs)); +} + +template auto operator|=(E &rhs, const E &lhs) -> E & { + rhs = rhs | lhs; + return rhs; +} + +template auto operator^(const E &rhs, const E &lhs) -> E { + return static_cast(underlying_type_cast(rhs) ^ underlying_type_cast(lhs)); +} + +template auto operator^=(E &rhs, const E &lhs) -> E & { + rhs = rhs ^ lhs; + return rhs; +} + +template auto operator~(const E &rhs) -> E { + return static_cast(~underlying_type_cast(rhs)); +} + +template auto is_empty(const E &value) -> bool { + return std::to_underlying(value) == 0; +} + +} // namespace bitflag_operators + +using bitflag_operators::enum_type; + +template +auto underlying_type_cast(const E &value) -> std::underlying_type_t +requires std::is_enum_v { + return static_cast>(value); +} + +template +requires std::is_enum_v +class EnumFlag { + E flags; + +public: + using U = std::underlying_type_t; + using value_type = U; + + EnumFlag() : flags(static_cast(U(0))) {} + EnumFlag(const U &value) : flags(static_cast(value)) {} + EnumFlag(const E &value) : flags(value) {} + + auto operator=(const U &value) -> EnumFlag & { + flags = static_cast(value); + return *this; + } + auto operator=(const E &value) -> EnumFlag & { + flags = value; + return *this; + } + + auto as_underlying_type() const -> U { return static_cast(flags); } + auto as_enum_type() const -> E { return flags; } + + /// `union` is reserved + auto unison(const EnumFlag &other) const -> EnumFlag { return this | other; } + + auto intersection(const EnumFlag &other) const -> EnumFlag { + return *this & other; + } + + auto contains(const EnumFlag &other) const -> bool { + return intersection(other).as_underlying_type() == + other.as_underlying_type(); + } + + auto intersects(const EnumFlag &other) const -> bool { + return intersection().as_underlying_type() != 0; + } + + auto add(const EnumFlag &other) -> EnumFlag & { + *this |= other; + return *this; + } + + auto remove(const EnumFlag &other) -> EnumFlag & { + *this &= ~other; + return *this; + } + + operator U() const { return as_underlying_type(); } + operator E() const { return as_enum_type(); } + + // auto operator==(const EnumFlag& other) const { + // return as_underlying_type() == other.as_underlying_type(); + // } + + auto operator<=>(const EnumFlag &other) const { + return other.as_underlying_type() <=> other.as_underlying_type(); + } + + // operator bool() const { return as_underlying_type() != 0; } + + auto operator|=(const EnumFlag &other) { + flags = static_cast(as_underlying_type() | other.as_underlying_type()); + } + + auto operator|(const EnumFlag &other) const -> EnumFlag { + auto flag = *this; + flag |= other; + + return flag; + } + + auto operator&=(const EnumFlag &other) { + flags = static_cast(as_underlying_type() & other.as_underlying_type()); + } + + auto operator&(const EnumFlag &other) const -> EnumFlag { + auto flag = *this; + flag &= other; + + return flag; + } + + auto operator~() const -> EnumFlag { + return static_cast(~as_underlying_type()); + } + + auto operator^=(const EnumFlag &other) { + flags = static_cast(as_underlying_type() ^ other.as_underlying_type()); + } + + auto operator^(const EnumFlag &other) const -> EnumFlag { + auto flag = *this; + flag ^= other; + + return flag; + } +}; +} // namespace util \ No newline at end of file diff --git a/include/format.hpp b/include/format.hpp new file mode 100644 index 0000000..d8c74cf --- /dev/null +++ b/include/format.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "move.hpp" +#include "type_traits.hpp" +#include +#include + +namespace util { + +template +inline constexpr auto format(std::string_view fmt, Ts&&... args) + -> std::string { +return std::vformat(fmt, std::make_format_args(std::forward(args)...)); +} + +template +using has_formatter = + std::is_constructible>; + +template + using is_formattable = has_formatter; + + template + inline constexpr bool is_formattable_v = is_formattable::value; + + template + concept formattable = is_formattable_v; +} // namespace util + +#include + +template +struct std::formatter> : public std::formatter { + template + auto format(const std::optional& option, FormatContext& ctx) + -> decltype(ctx.out()) { + auto fmt = ctx.out(); + + if (option.has_value()) { + fmt = std::format_to(fmt, "Some("); + ctx.advance_to(fmt); + fmt = std::formatter::format(option.value(), ctx); + *fmt++ = ')'; + } else { + fmt = std::format_to(fmt, "None"); + } + + return fmt; + } +}; \ No newline at end of file diff --git a/include/iterator.hpp b/include/iterator.hpp new file mode 100644 index 0000000..949acfa --- /dev/null +++ b/include/iterator.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace util { + +template +decltype(auto) get(Ts&&... ts) { + return std::get(std::forward_as_tuple(ts...)); +} + +template +class Range { + Iterator m_begin, m_end; + + public: + Range(const Iterator _begin, const Iterator _end) + : m_begin(_begin), m_end(_end) {} + + Range(const std::pair& t) { + m_begin = t.first; + m_end = t.second; + } + + template + Range(Args&&... args) : m_begin(get<0>(args...)), m_end(get<1>(args...)) {} + + auto begin() -> Iterator { return m_begin; } + auto begin() const -> const Iterator { return m_begin; } + + auto end() -> Iterator { return m_end; } + auto end() const -> const Iterator { return m_end; } +}; + +} // namespace util diff --git a/include/math.hpp b/include/math.hpp new file mode 100644 index 0000000..0b9fec2 --- /dev/null +++ b/include/math.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "types.hpp" +#include +#include + + +namespace util { +template auto radians(N degrees) -> N { + return (degrees * std::numbers::pi_v) / static_cast(180); +} + +template auto degrees(N radians) -> N { + return (radians * static_cast(180)) / std::numbers::pi_v; +} +} // namespace util \ No newline at end of file diff --git a/include/move.hpp b/include/move.hpp new file mode 100644 index 0000000..ed0c28e --- /dev/null +++ b/include/move.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "type_traits.hpp" + +// static_cast to rvalue reference +#define MOV(...) \ + static_cast&&>(__VA_ARGS__) + +// static_cast to identity +// The extra && aren't necessary as discussed above, but make it more robust in case it's used with a non-reference. +#define FWD(...) \ + static_cast(__VA_ARGS__) \ No newline at end of file diff --git a/include/option.hpp b/include/option.hpp new file mode 100644 index 0000000..8051df1 --- /dev/null +++ b/include/option.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace util { + template + class Option { + public: + + +private: +bool m_has_value; +T m_value; + }; +} \ No newline at end of file diff --git a/include/print.hpp b/include/print.hpp new file mode 100644 index 0000000..509a6ce --- /dev/null +++ b/include/print.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "format.hpp" + +namespace util { + +template +inline constexpr auto print(std::string_view fmt, Ts&&... args) + ->void{ + const auto str = util::format(fmt, std::forward(args)...); + std::cout.write(str.c_str(), str.size()); +} + +template +inline constexpr auto println(std::string_view fmt, Ts&&... args) + ->void{ + print(fmt, std::forward(args)...); + std::cout.put('\n'); +} + +} \ No newline at end of file diff --git a/include/ranges/collect.hpp b/include/ranges/collect.hpp new file mode 100644 index 0000000..b52df72 --- /dev/null +++ b/include/ranges/collect.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include "../concepts/container.hpp" +#include +#include +#include +#include + + +namespace util { +namespace rg = std::ranges; +namespace detail { +template +constexpr auto collect_range(R &&r) -> Collection { + Collection c; + if constexpr (rg::sized_range && util::reservable) { + c.reserve(r.size()); + } + + if constexpr (util::back_insertible) { + rg::move(r, std::back_inserter(c)); + } else if constexpr (util::back_emplaceable) { + for (auto &&e : r) { + c.emplace_back(std::move(e)); + } + } else if constexpr (util::emplaceable) { + for (auto &&e : r) { + c.emplace(std::move(e)); + } + } + + return c; +} + +template struct collect_range_adaptor { + template constexpr auto operator()(R &&r) const { + return collect_range(std::forward(r)); + } +}; + +template +constexpr auto operator|(R &&r, + const collect_range_adaptor &adaptor) { + return adaptor(std::forward(r)); +} +} // namespace detail + +namespace views { + +template +inline detail::collect_range_adaptor collect; +} + +template + +constexpr auto collect(R &&r) -> Collection { + return r | views::collect; +} + +template +constexpr auto to_vector(Range r) + -> std::vector> { + std::vector> v; + + if constexpr (rg::sized_range) { + v.reserve(rg::size(r)); + } + + std::ranges::copy(r, std::back_inserter(v)); + + return v; +} + +template +constexpr auto to_unordered_set(Range r) + -> std::unordered_set> { + std::unordered_set> v(r.begin(), + r.end()); + return v; +} + +} // namespace util \ No newline at end of file diff --git a/include/ranges/drop_last.hpp b/include/ranges/drop_last.hpp new file mode 100644 index 0000000..70df1ed --- /dev/null +++ b/include/ranges/drop_last.hpp @@ -0,0 +1,68 @@ +#pragma once +#include + +namespace util { +namespace rg = std::ranges; + +template +requires rg::view +class drop_last_view : public rg::view_interface> { +private: + R base_{}; + std::iter_difference_t> count_{}; + rg::iterator_t iter_{std::begin(base_)}; + +public: + drop_last_view() = default; + + constexpr drop_last_view(R base, + std::iter_difference_t> count) + : base_(base), count_(count), iter_(std::begin(base_)) {} + + constexpr R base() const & { return base_; } + constexpr R base() && { return std::move(base_); } + + constexpr auto begin() const { return iter_; } + constexpr auto end() const { return std::end(base_) - count_; } + + constexpr auto size() const requires rg::sized_range { + const auto s = rg::size(base_); + const auto c = static_cast(count_); + return s < c ? 0 : s - c; + } +}; + +namespace detail { +struct drop_last_range_adaptor_closure { + std::size_t count_; + constexpr drop_last_range_adaptor_closure(std::size_t count) + : count_(count) {} + + template constexpr auto operator()(R &&r) const { + return drop_last_view(std::forward(r), count_); + } +}; + +struct drop_last_range_adaptor { + template + constexpr auto operator()(R &&r, + std::iter_difference_t> count) { + return drop_last_view(std::forward(r), count); + } + + constexpr auto operator()(std::size_t count) { + return drop_last_range_adaptor_closure(count); + } +}; + +template +constexpr auto operator|(R &&r, drop_last_range_adaptor_closure const &a) { + return a(std::forward(r)); +} +} // namespace detail + +namespace views { +inline detail::drop_last_range_adaptor drop_last; +} // namespace views + +} // namespace util \ No newline at end of file diff --git a/include/ranges/enumerate.hpp b/include/ranges/enumerate.hpp new file mode 100644 index 0000000..2f9f478 --- /dev/null +++ b/include/ranges/enumerate.hpp @@ -0,0 +1,91 @@ +#pragma once +#include "types.hpp" +#include + + +namespace util { +namespace rg = std::ranges; + +template struct enumerate_iterator_t : rg::iterator_t { + using base = rg::iterator_t; + using enumerator_type = std::size_t; + using value_type = std::pair>; + using reference = + std::pair>>; + + enumerator_type enumerator{}; + + enumerate_iterator_t() = default; + enumerate_iterator_t(const base &b) : base{b}, enumerator(0) {} + + auto operator<=>(const enumerate_iterator_t &other) const { + return static_cast(*this) <=> + static_cast(other); + } + + auto inc_enumerator() -> void { enumerator++; } + + auto operator++(int) -> enumerate_iterator_t { + const auto result = static_cast(*this)++; + inc_enumerator(); + return result; + } + + auto operator++() -> enumerate_iterator_t & { + inc_enumerator(); + ++static_cast(*this); + return (*this); + } + + auto operator*() -> reference { + return reference(enumerator, *static_cast(*this)); + } + auto operator*() const -> reference { + return reference(enumerator, *static_cast(*this)); + } +}; + +template +class enumerate_view : public rg::view_interface> { + R base_{}; + isize enumerator{}; + enumerate_iterator_t iter_{std::begin(base_)}; + +public: + enumerate_view() = default; + + constexpr enumerate_view(R base) : base_(base), iter_(std::begin(base_)) {} + + constexpr R base() const & { return base_; } + constexpr R base() && { return std::move(base_); } + + constexpr auto begin() const { return iter_; } + constexpr auto end() const { return std::end(base_); } +}; + +namespace detail { +struct enumerate_range_adaptor_closure { + template constexpr auto operator()(R &&r) const { + return enumerate_view(std::forward(r)); + } +}; + +struct enumerate_range_adaptor { + template constexpr auto operator()(R &&r) { + return enumerate_view(std::forward(r)); + } + + constexpr auto operator()() { return enumerate_range_adaptor_closure(); } +}; + +template +constexpr auto operator|(R &&r, enumerate_range_adaptor_closure const &a) { + return a(std::forward(r)); +} +} // namespace detail + +namespace views { +inline detail::enumerate_range_adaptor_closure enumerate; +} // namespace views + +} // namespace util \ No newline at end of file diff --git a/include/ranges/first.hpp b/include/ranges/first.hpp new file mode 100644 index 0000000..338e8b0 --- /dev/null +++ b/include/ranges/first.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + + +namespace util { +namespace rg = std::ranges; +namespace detail { + +template +constexpr auto get_first(R &&r) -> std::optional> { + if (rg::begin(r) == rg::end(r)) { + return std::nullopt; + } else { + return *rg::begin(r); + } +} + +struct first_range_adaptor { + template constexpr auto operator()(R &&r) const { + return get_first(std::forward(r)); + } +}; + +template +constexpr auto operator|(R &&r, const first_range_adaptor &adaptor) { + return adaptor(std::forward(r)); +} +} // namespace detail + +namespace views { + +inline detail::first_range_adaptor first; +} + +template +constexpr auto get_first(R &&r) -> std::optional>{ + return r | views::first; +} +} // namespace util \ No newline at end of file diff --git a/include/ranges/generated.hpp b/include/ranges/generated.hpp new file mode 100644 index 0000000..ac92904 --- /dev/null +++ b/include/ranges/generated.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include + + +namespace util { +namespace rg = std::ranges; + +namespace detail { + +template struct generated_iterator { + using value_type = decltype(std::declval()()); + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + value_type value{}; + F generator{}; + + generated_iterator() = default; + generated_iterator(const F &gen) : generator(gen), value(gen()) {} + generated_iterator(F &&gen) : generator(std::move(gen)), value(gen()) {} + + auto generate_new_value() -> void { value = generator(); } + + constexpr auto operator==(const generated_iterator &other) const { + return false; + } + + auto operator++(int) -> generated_iterator { + const auto result = *this; + ++(*this); + return result; + } + + auto operator++() -> generated_iterator & { + generate_new_value(); + return (*this); + } + + auto operator*() { return value; } + auto operator*() const { return value; } +}; +} // namespace detail + +template +class generated_view : rg::view_interface> { + F generator; + +public: + generated_view() = default; + constexpr generated_view(F &&generator) : generator(generator) {} + + constexpr auto begin() const { return detail::generated_iterator(generator); } + constexpr auto end() const { return std::unreachable_sentinel; } +}; + +namespace detail { + +struct generated_range_adaptor_closure { + template constexpr auto operator()(F &&fn) const { + return generated_view(std::forward(fn)); + } +}; + +struct generated_range_adaptor { + template constexpr auto operator()(F &&r) { + return generated_view(std::forward(r)); + } + + constexpr auto operator()() { return generated_range_adaptor_closure(); } +}; +} // namespace detail + +namespace views { + +inline detail::generated_range_adaptor_closure from; +} + +} // namespace util \ No newline at end of file diff --git a/include/result.hpp b/include/result.hpp new file mode 100644 index 0000000..d6a7143 --- /dev/null +++ b/include/result.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace util { +class Result {}; + +using result = Result; +using expected = Result; +} // namespace util \ No newline at end of file diff --git a/include/source_location.hpp b/include/source_location.hpp new file mode 100644 index 0000000..31b60a4 --- /dev/null +++ b/include/source_location.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "types.hpp" + +namespace util { + +struct source_location { + _NODISCARD static constexpr source_location + current(const uint_least32_t _Line_ = __builtin_LINE(), + const uint_least32_t _Column_ = __builtin_COLUMN(), + const char *const _File_ = __builtin_FILE(), + const char *const _Function_ = __builtin_FUNCTION()) noexcept { + source_location _Result; + _Result._Line = _Line_; + _Result._Column = _Column_; + _Result._File = _File_; + _Result._Function = _Function_; + return _Result; + } + + _NODISCARD_CTOR constexpr source_location() noexcept = default; + + _NODISCARD constexpr uint_least32_t line() const noexcept { return _Line; } + _NODISCARD constexpr uint_least32_t column() const noexcept { + return _Column; + } + _NODISCARD constexpr const char *file_name() const noexcept { return _File; } + _NODISCARD constexpr const char *function_name() const noexcept { + return _Function; + } + +private: + uint_least32_t _Line{}; + uint_least32_t _Column{}; + const char *_File = ""; + const char *_Function = ""; +}; +} \ No newline at end of file diff --git a/include/step_function.hpp b/include/step_function.hpp new file mode 100644 index 0000000..dcde6fb --- /dev/null +++ b/include/step_function.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include "assert.hpp" +#include "step_function.hpp" +#include "types.hpp" +#include +#include +#include +#include "time.hpp" + +enum class ReturnTag { + Repeat, + Next, + Previous, + Goto, +}; + +struct Return { + ReturnTag tag; + union { + isize index; + }; +}; + +using namespace std::chrono_literals; + +class Stepped { + +using duration_type = std::chrono::system_clock::duration; + using step_return_type = std::pair; + using step_type = std::function; + + std::vector m_steps; + + isize m_current_step; + duration_type m_current_wait_duration; + util::Delta m_delta; + + auto inc_step() -> void; + auto dec_step() -> void; + + public: + Stepped() : m_current_step(0), m_steps(), m_delta(), m_current_wait_duration(0ms) {} + + auto add_step(const step_type& step) -> void; + auto run() -> void; + auto reset() -> void; + + +}; + +/// IMPLEMENTATION + +using namespace std::chrono_literals; + +auto Stepped::inc_step() -> void { + if (++m_current_step >= m_steps.size()) { + m_current_step = 0; + } +} + +auto Stepped::dec_step() -> void { + if (--m_current_step < 0) { + m_current_step = m_steps.size() - 1; + } +} + +auto Stepped::add_step(const step_type &step) -> void { + m_steps.push_back(step); +} + +auto Stepped::reset() -> void { + m_current_step = 0; + m_current_wait_duration = 0ms; +} + +auto Stepped::run() -> void { + if (m_delta.has_elapsed(m_current_wait_duration)) { + util::assert(m_current_step < m_steps.size()); + + auto&& [result, next_wait_duration] = m_steps[m_current_step](); + + switch (result.tag) { + case ReturnTag::Repeat: + break; + case ReturnTag::Next: + inc_step(); + break; + case ReturnTag::Previous: + dec_step(); + break; + case ReturnTag::Goto: { + util::assert(result.index < m_steps.size() && result.index >= 0); + m_current_step = result.index; + break; + } + } + + m_current_wait_duration = next_wait_duration; + } +} \ No newline at end of file diff --git a/include/string.hpp b/include/string.hpp new file mode 100644 index 0000000..31b9cab --- /dev/null +++ b/include/string.hpp @@ -0,0 +1,151 @@ +#pragma once + +#include "types.hpp" +#include +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#endif + +#include +#include +#include +#include +#include + +namespace util { +#ifdef _WIN32 +inline std::string to_string(std::wstring_view wstr) { + const auto size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(), + nullptr, 0, nullptr, nullptr); + + std::string str(size, 0); + + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(), str.data(), + str.length(), nullptr, nullptr); + + return str; +} + +inline std::wstring to_wstring(std::string_view str) { + const auto size = + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), nullptr, 0); + + std::wstring wstr(size, 0); + + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), wstr.data(), + wstr.length()); + + return wstr; +} +#endif + +template +std::basic_string to_lower(std::basic_string_view view) { + std::basic_string out(view.size(), '\0'); + + std::transform( + std::begin(view), std::end(view), std::begin(out), + [](typename std::basic_string::value_type c) { + return std::use_facet< + std::ctype::value_type>>( + std::locale()) + .tolower(c); + }); + + return out; +} + +template +constexpr bool string_eq_nocase(std::basic_string_view rh, + std::basic_string_view lh) { + const auto rh_lower = to_lower(rh); + const auto lh_lower = to_lower(lh); + + return rh_lower == lh_lower; +} + +template +auto split_with(const std::basic_string &str, CharT delimiter) + -> std::vector> { + std::vector> parts; + std::basic_string part; + + const auto start = str.find_first_not_of(delimiter); + const auto end = str.find_last_not_of(delimiter); + std::basic_istringstream stream( + str.substr(start, end == std::string::npos ? end : end + 1)); + + while (std::getline(stream, part, delimiter)) + parts.push_back(part); + + return parts; +} + +#include + +template +inline constexpr auto from_char_radix(const char &c, I radix = 10) -> I { + I value = 0; + + if (std::isdigit(c)) { + value = c - '0'; + } else if (std::isalpha(c)) { + if (std::isupper(c)) { + value = c + 10 - 'A'; + } else { + value = c + 10 - 'a'; + } + } + + return value < radix ? value : std::numeric_limits::max(); +} + +template +inline constexpr auto from_string_view_radix(std::string_view view, + I radix = 10) -> I { + auto negative = false; + if (view.starts_with('-')) { + negative = true; + view.remove_prefix(1); + } else if (view.starts_with('+')) { + negative = false; + view.remove_prefix(1); + } + + I value = 0; + const auto max = std::numeric_limits::max(); + + for (auto &&c : view) { + const auto i = from_char_radix(c, radix); + + if (i >= radix) { + return max; + } + + if (value > max / radix) { + return max; + } + + value *= radix; + + if (i > max - value) { + return max; + } + + value += i; + } + + if (negative && std::is_signed_v) { + return -value; + } else { + return value; + } +} + +} // namespace util diff --git a/include/tagged_union.hpp b/include/tagged_union.hpp new file mode 100644 index 0000000..df38656 --- /dev/null +++ b/include/tagged_union.hpp @@ -0,0 +1,124 @@ +#pragma once + +#include "assert.hpp" +#include "enum.hpp" +#include "tagged_union.hpp" +#include "type_traits.hpp" +#include "types.hpp" +#include +#include +#include +#include +#include + +namespace util { +namespace detail { +template +using nth_type = typename std::tuple_element>::type; + +template consteval std::size_t locate(std::size_t ind) { + return static_cast(-1); +} + +template +consteval std::size_t locate(std::size_t ind = 0) { + if (std::is_same::value) { + return ind; + } else { + return locate(ind + 1); + } +} + +template +struct index_of + : std::integral_constant>> {}; + +template + inline constexpr std::size_t index_of_v = index_of::value; + + +template struct is_in : std::false_type {}; + +template +struct is_in + : std::integral_constant::value || + is_in::value> {}; + + +template +concept in_types = is_in::value; + + +template +inline constexpr auto max_all_inner(T arg) -> T { + return arg; +} + +template +inline constexpr auto max_all_inner(T arg, Ts... args) -> decltype(max_all_inner(arg)){ + return std::max(arg, max_all_inner(args...)); +} + +template +inline constexpr auto max_all(Ts... args) -> usize { + return max_all_inner(args...); +} + +template class variadic_union { + std::vector m_data; + std::optional m_type_index; + +public: + variadic_union() : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(std::nullopt) {} + + template + variadic_union(Us&&... args) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) { + *reinterpret_cast*>(m_data.data()) = detail::nth_type{std::forward(args)...}; + } + + template + requires (std::same_as>) + variadic_union(const U& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) { + *reinterpret_cast*>(m_data.data()) = value; + } + + template + requires (std::same_as>) + variadic_union(U&& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) { + *reinterpret_cast*>(m_data.data()) = std::move(value); + } + + template + requires (in_types) + variadic_union(U&& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(index_of_v) { + *reinterpret_cast(m_data.data()) = std::move(value); + } + + template + requires (in_types) + variadic_union(const U& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(index_of_v) { + *reinterpret_cast(m_data.data()) = value; + } + +template +constexpr auto get() -> U& { + util::assert_eq(m_type_index.value(), index_of_v); + + return reinterpret_cast(m_data.data()); +} +}; + +} // namespace detail + +#define NTH(Is, Ts) typename detail::nth_type<, Ts...> element_##I; + +template class TaggedUnion { + Tag tag; + detail::variadic_union inner; +public: +template + requires (detail::in_types) +TaggedUnion(const Tag& tag, const U& value) : tag(tag), inner(detail::variadic_union(value)) {} + +}; +} // namespace util \ No newline at end of file diff --git a/include/thread.hpp b/include/thread.hpp new file mode 100644 index 0000000..76bb7c7 --- /dev/null +++ b/include/thread.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include "types.hpp" +#include +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +#include + +namespace util { +namespace threads { + +auto get_all_threads(u32 pid) -> std::vector; + +auto get_all_threads() -> std::vector; +auto get_other_threads() -> std::vector; + +auto suspend_threads(const std::vector &thread_ids) -> std::vector; + +auto resume_threads(const std::vector &thread_ids) -> bool; + +/// suspends all threads, only then executes function, and resumes threads +/// returns `nullopt` if not all threads were suspended. +template +auto with_suspended_threads(F &&fn) -> std::optional { + auto threads = get_other_threads(); + + auto suspended_threads = suspend_threads(threads); + + const auto suspended_all_threads = suspended_threads.size() == threads.size(); + + std::optional result = std::nullopt; + if (suspended_all_threads) { + result = fn(); + } + + return result; +} + +} // namespace threads + +namespace processes { +auto get_process_id(std::string_view name) -> std::optional; +auto get_process_id(std::wstring_view name) -> std::optional; +} // namespace processes + +/// impl + +namespace threads { +inline auto get_all_threads(u32 pid) -> std::vector { + std::vector threads; + const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + + if (snapshot != INVALID_HANDLE_VALUE) { + THREADENTRY32 t; + t.dwSize = sizeof(t); + if (Thread32First(snapshot, &t)) { + if (t.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + + sizeof(t.th32OwnerProcessID)) { + if (t.th32OwnerProcessID == pid) { + threads.push_back(t.th32ThreadID); + } + } + } + } + + return threads; +} + +inline auto get_all_threads() -> std::vector { + return get_all_threads(GetCurrentProcessId()); +} + +inline auto get_other_threads() -> std::vector { + auto threads = get_all_threads(GetCurrentProcessId()); + + threads.erase( + std::remove_if(threads.begin(), threads.end(), + [](auto &&id) { return id == GetCurrentThreadId(); }), + threads.end()); + + return threads; +} + +inline auto resume_threads(const std::vector &thread_ids) -> bool { + // return false if any threads failed to resume + return std::count_if(thread_ids.begin(), thread_ids.end(), [](auto &&id) { + if (auto handle = OpenThread(THREAD_SUSPEND_RESUME, false, id)) { + const auto result = ResumeThread(handle); + CloseHandle(handle); + + return result != (u32)-1; + } + return false; + }) == thread_ids.size(); +} + +inline auto suspend_threads(const std::vector &thread_ids) + -> std::vector { + std::vector suspended_threads; + for (auto &&id : thread_ids) { + if (auto handle = OpenThread(THREAD_SUSPEND_RESUME, false, id)) { + if (SuspendThread(handle) != (u32)-1) { + suspended_threads.push_back(id); + } + CloseHandle(handle); + } + } + return suspended_threads; +} +} // namespace threads + +namespace processes { + +inline auto get_process_id(std::string_view name) -> std::optional { + auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + std::optional result = std::nullopt; + + PROCESSENTRY32 process_entry; + if (Process32First(snapshot, &process_entry)) { + do { + const auto process_name = std::string_view(process_entry.szExeFile); + if (name == process_name) { + result = process_entry.th32ProcessID; + } + } while (Process32Next(snapshot, &process_entry)); + } + + CloseHandle(snapshot); + + return result; +} + +inline auto get_process_id(std::wstring_view name) -> std::optional { + auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + std::optional result = std::nullopt; + + PROCESSENTRY32W process_entry; + if (Process32FirstW(snapshot, &process_entry)) { + do { + const auto process_name = std::wstring_view(process_entry.szExeFile); + if (name == process_name) { + result = process_entry.th32ProcessID; + } + } while (Process32NextW(snapshot, &process_entry)); + } + + CloseHandle(snapshot); + + return result; +} + +} // namespace processes + +} // namespace util +#endif \ No newline at end of file diff --git a/include/time.hpp b/include/time.hpp new file mode 100644 index 0000000..82ddd98 --- /dev/null +++ b/include/time.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "types.hpp" +#include +#include + +namespace util { +using std::chrono::system_clock; +using namespace std::chrono_literals; + +template +auto time_of(F &&fn) -> std::chrono::duration { + const auto start = std::chrono::system_clock::now(); + fn(); + const auto end = std::chrono::system_clock::now(); + + return {end - start}; +} + +class Delta { + system_clock::time_point last; + +public: + Delta() : last(system_clock::now()) {} + + template + inline auto has_elapsed(const std::chrono::duration &duration) + -> bool { + const auto now = system_clock::now(); + + if (duration < (now - last)) { + last = now; + return true; + } else { + return false; + } + } + + template + inline auto elapsed() -> std::chrono::duration { + const auto now = system_clock::now(); + const auto duration = + std::chrono::duration_cast>(now - + last); + last = now; + + return duration; + } + + inline auto has_elapsed_s(f64 seconds) -> bool { + return has_elapsed(std::chrono::duration(seconds)); + } + + inline auto has_elapsed_ms(i64 milliseconds) -> bool { + return has_elapsed(std::chrono::duration(milliseconds)); + } + + inline auto elapsed_ms() -> std::chrono::duration { + const auto now = system_clock::now(); + const auto duration = std::chrono::duration_cast( + system_clock::now() - last); + last = now; + return duration; + } + + inline auto elapsed_s() -> std::chrono::duration { + const auto now = system_clock::now(); + const auto duration = + std::chrono::duration(system_clock::now() - last); + last = now; + return duration; + } +}; +} // namespace util diff --git a/include/type_traits.hpp b/include/type_traits.hpp new file mode 100644 index 0000000..5e2e032 --- /dev/null +++ b/include/type_traits.hpp @@ -0,0 +1,78 @@ +#pragma once + +namespace util { + + +template +struct integral_constant {static constexpr T value = V;}; + +struct true_type : integral_constant {}; +struct false_type : integral_constant {}; + +inline constexpr bool true_type_v = true_type::value; +inline constexpr bool false_type_v = false_type::value; + +namespace detail { +template struct type_identity { + using type = T; +}; // or use std::type_identity (since C++20) + +template // Note that `cv void&` is a substitution failure +auto try_add_lvalue_reference(int) -> type_identity; +template // Handle T = cv void case +auto try_add_lvalue_reference(...) -> type_identity; + +template auto try_add_rvalue_reference(int) -> type_identity; +template auto try_add_rvalue_reference(...) -> type_identity; + +} // namespace detail + +template +struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference(0)) { +}; + +template +struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference(0)) { +}; + +template +using add_rvalue_reference_t = typename add_rvalue_reference::type; + +template add_rvalue_reference_t declval() noexcept { + #pragma clang diagnostic push + static_assert(false, "Calling declval is ill-formed, see N4892 [declval]/2."); + #pragma clang diagnostic pop +} + +namespace detail { + +template +auto test_returnable(int) + -> decltype(void(static_cast(nullptr)), true_type{}); +template auto test_returnable(...) -> false_type; + +template +auto test_implicitly_convertible(int) + -> decltype(void(declval()(declval())), + true_type{}); +template +auto test_implicitly_convertible(...) -> false_type; + +} // namespace detail + +template +struct is_void : false_type{}; + +template<> +struct is_void : true_type {}; + +template +inline constexpr bool is_void_v = is_void::value; + +template< class T > struct remove_reference { typedef T type; }; +template< class T > struct remove_reference { typedef T type; }; +template< class T > struct remove_reference { typedef T type; }; + +template< class T > +using remove_reference_t = typename remove_reference::type; +} \ No newline at end of file diff --git a/include/types.hpp b/include/types.hpp new file mode 100644 index 0000000..41713c4 --- /dev/null +++ b/include/types.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +using byte = unsigned char; + +using u8 = uint8_t; +using i8 = int8_t; + +using u16 = uint16_t; +using i16 = int16_t; + +using u32 = uint32_t; +using i32 = int32_t; + +using u64 = uint64_t; +using i64 = int64_t; + +using usize = std::size_t; +using isize = std::ptrdiff_t; + +using f32 = float; +using f64 = double; diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..418cd5c --- /dev/null +++ b/src/main.cc @@ -0,0 +1,50 @@ +#include "format.hpp" +#include "print.hpp" +#include "ranges/first.hpp" +#include "ranges/generated.hpp" +#include "tagged_union.hpp" +#include +#include +#include + +#define UTIL_ASSERT_FORMAT +#include "assert.hpp" +#include "ranges/collect.hpp" +#include +#include +#include +#include + +using namespace std::string_view_literals; + +int main(int argc, const char *argv[]) { + printf("Hello, Alloy!\n"); + auto vec = std::vector({1, 2, 3, 4, 5}); + + auto range = vec | std::views::filter([](auto &i) { return i % 2; }); + auto infinite_range = util::views::from([] {return 1;}) | std::views::take(5); + + auto random = util::views::from([] { + return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" + [std::rand() % + std::strlen( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY")]; + }) | + std::views::take(10) | util::views::collect; + +auto first = random | util::views::first; + +util::println("{}", first); +util::println("{}", random); + + for (auto&& i : infinite_range) { + util::print("{}\n", i); + } + +util::print("hello {}\n", "world"); + + util::assert_eq("asdf"sv, "nsdf"sv); + + const auto set = util::collect>(range); + return 1; +} \ No newline at end of file