commit 62272807dcebbec96c2fbabc172c36e0eccaeae3 Author: janis <janis@nirgendwo.xyz> Date: Thu Jun 23 17:26:19 2022 +0100 initial commit 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 <stdio.h> +#include "move.hpp" +#include "source_location.hpp" +#include "type_traits.hpp" + +namespace util { +template <class From, class To> +struct is_convertible + : std::integral_constant< + bool, + (decltype(detail::test_returnable<To>(0))::value && + decltype(detail::test_implicitly_convertible<From, To>(0))::value) || + (is_void<From>::value && is_void<To>::value)> {}; + +template <typename From, typename To> +inline constexpr bool is_convertible_v = is_convertible<From, To>::value; + +template <typename From> +concept boolean_testable_impl = is_convertible_v<From, bool> && requires { + static_cast<bool>(declval<From>()); +}; + +template <typename T> +concept boolean_testable = requires(T &&t) { + { !static_cast<T &&>(t) } -> boolean_testable_impl; +}; + +template <typename A, typename B> +concept weakly_equality_comparable_with = requires (const remove_reference_t<A>& a, const remove_reference_t<B>& b) { + {a == b} -> boolean_testable; + {a != b} -> boolean_testable; + {b == a} -> boolean_testable; + {b == a} -> boolean_testable; +}; + +template <typename T> +concept equality_comparable = weakly_equality_comparable_with<T, T>; + +template <typename A, typename B> +concept equality_comparable_with = weakly_equality_comparable_with<A, B>; + +template <boolean_testable B> +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<B>) { + 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 <typename A, typename B> +requires (equality_comparable_with<A, B>) +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<A> && is_formattable_v<B>) { + 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 <equality_comparable T> +inline constexpr auto assert_eq(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void { + assert_eq<T,T>(FWD(rhs), FWD(lhs),source); +} + + +template <typename A, typename B> +requires (equality_comparable_with<A, B>) +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<A> && is_formattable_v<B>) { + 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 <equality_comparable T> +inline constexpr auto assert_ne(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void { + assert_ne<T, T>(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 <concepts> +#include <ranges> + +namespace util { +template <typename C> +concept reservable = requires(C &c, typename C::size_type i) { + {c.reserve(i)}; +}; + +template <typename C> +concept back_insertible = requires(C &c, + const std::ranges::range_value_t<C> &t) { + { std::back_inserter(c) } -> std::same_as<std::back_insert_iterator<C>>; + {c.push_back(t)}; +}; + +template <typename C> +concept front_insertible = requires(C &c, + const std::ranges::range_value_t<C> &t) { + { std::front_inserter(c) } -> std::same_as<std::front_insert_iterator<C>>; + {c.push_front(t)}; +}; + +template <typename C> +concept insertible = requires(C &c, std::ranges::iterator_t<C> i, + const std::ranges::range_value_t<C> &t) { + { std::inserter(c, i) } -> std::same_as<std::insert_iterator<C>>; + {c.insert(i, t)}; +}; +template <typename C, typename... Ts> +concept emplaceable = requires(C &c, Ts... t) { + {c.emplace(std::move(t...))}; +}; + +template <typename C, typename... Ts> +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 <compare> +#include <concepts> +#include <type_traits> + +namespace util { + +template <typename T> +concept Number = std::integral<T> || std::floating_point<T>; + +template <auto T, auto U> +requires std::equality_comparable_with<decltype(T), decltype(T)> +struct is_equal : public std::integral_constant<bool, T == U> { +}; + +template <auto T, auto U> +inline constexpr auto is_equal_v = is_equal<T, U>::value; + +template <auto T, auto U> +requires std::three_way_comparable_with<decltype(T), decltype(T)> +struct cmp { + constexpr static std::partial_ordering ordering = T <=> U; +}; + +template <auto T, auto U> inline constexpr auto cmp_v = cmp<T, U>::ordering; + +template <std::partial_ordering ordering, auto T, auto U> +concept Compare = is_equal_v<cmp_v<T, U>, ordering>; + +template <auto T, auto U> +concept Equal = is_equal_v<T, U>; + +template <typename T, typename... Pack> +concept all_same_as = (std::same_as<T, Pack> && ...); + +template <typename T, typename U> +concept explicit_convertible_to = requires(const T ct, T t, const U &cu) { + {t = cu}; + { ct.operator U() } -> std::same_as<U>; +} +|| std::same_as<T, U>; + +template <typename From, typename... To> +concept explicit_convertible_to_any_of = + (explicit_convertible_to<std::remove_cv_t<From>, To> || ...); + +template <typename From, typename... To> +struct is_explicit_convertible_to_any_of + : std::bool_constant<explicit_convertible_to_any_of<From, To...>> {}; + +template <typename T> +concept explicit_convertible_to_integral = + std::integral<T> || explicit_convertible_to_any_of < std::remove_cv_t<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 <typename T> +concept explicit_convertible_to_floating_point = std::floating_point<T> || + explicit_convertible_to_any_of<T, float, double, long double>; + +template <typename From, typename To> +struct is_explicit_convertible + : std::bool_constant<explicit_convertible_to<From, To>> {}; + +template <typename From, typename To> +inline constexpr bool is_explicit_convertible_v = + is_explicit_convertible<From, To>::value; + +template <typename Fn, typename R, typename... Args> +concept with_return_type = requires(Fn fn, Args... args) { + { fn(args...) } -> std::same_as<R>; +}; + +} // 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 <type_traits> +#include <utility> + +namespace util { +namespace bitflag_operators { + +template <typename E> +concept enum_type = std::is_enum_v<E>; + +template <enum_type E> auto operator&(const E &rhs, const E &lhs) -> E { + return static_cast<E>(underlying_type_cast(rhs) & underlying_type_cast(lhs)); +} + +template <enum_type E> auto operator&=(E &rhs, const E &lhs) -> E & { + rhs = rhs & lhs; + return rhs; +} + +template <enum_type E> auto operator|(const E &rhs, const E &lhs) -> E { + return static_cast<E>(underlying_type_cast(rhs) | underlying_type_cast(lhs)); +} + +template <enum_type E> auto operator|=(E &rhs, const E &lhs) -> E & { + rhs = rhs | lhs; + return rhs; +} + +template <enum_type E> auto operator^(const E &rhs, const E &lhs) -> E { + return static_cast<E>(underlying_type_cast(rhs) ^ underlying_type_cast(lhs)); +} + +template <enum_type E> auto operator^=(E &rhs, const E &lhs) -> E & { + rhs = rhs ^ lhs; + return rhs; +} + +template <enum_type E> auto operator~(const E &rhs) -> E { + return static_cast<E>(~underlying_type_cast(rhs)); +} + +template <enum_type E> auto is_empty(const E &value) -> bool { + return std::to_underlying(value) == 0; +} + +} // namespace bitflag_operators + +using bitflag_operators::enum_type; + +template <typename E> +auto underlying_type_cast(const E &value) -> std::underlying_type_t<E> +requires std::is_enum_v<E> { + return static_cast<std::underlying_type_t<E>>(value); +} + +template <typename E> +requires std::is_enum_v<E> +class EnumFlag { + E flags; + +public: + using U = std::underlying_type_t<E>; + using value_type = U; + + EnumFlag() : flags(static_cast<E>(U(0))) {} + EnumFlag(const U &value) : flags(static_cast<E>(value)) {} + EnumFlag(const E &value) : flags(value) {} + + auto operator=(const U &value) -> EnumFlag & { + flags = static_cast<E>(value); + return *this; + } + auto operator=(const E &value) -> EnumFlag & { + flags = value; + return *this; + } + + auto as_underlying_type() const -> U { return static_cast<U>(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<E>(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<E>(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<E>(~as_underlying_type()); + } + + auto operator^=(const EnumFlag &other) { + flags = static_cast<E>(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 <format> +#include <string_view> + +namespace util { + +template <typename... Ts> +inline constexpr auto format(std::string_view fmt, Ts&&... args) + -> std::string { +return std::vformat(fmt, std::make_format_args(std::forward<Ts>(args)...)); +} + +template <typename T, typename Context> +using has_formatter = + std::is_constructible<typename Context::template formatter_type<T>>; + +template <typename T> + using is_formattable = has_formatter<T, std::format_context>; + + template <typename T> + inline constexpr bool is_formattable_v = is_formattable<T>::value; + + template <typename T> + concept formattable = is_formattable_v<T>; +} // namespace util + +#include <optional> + +template<util::formattable F> +struct std::formatter<std::optional<F>> : public std::formatter<F> { + template <typename FormatContext> + auto format(const std::optional<F>& 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<F>::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 <tuple> + +namespace util { + +template <int I, class... Ts> +decltype(auto) get(Ts&&... ts) { + return std::get<I>(std::forward_as_tuple(ts...)); +} + +template <class Iterator> +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<const Iterator, const Iterator>& t) { + m_begin = t.first; + m_end = t.second; + } + + template <typename... Args> + 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 <concepts> +#include <numbers> + + +namespace util { +template <std::floating_point N> auto radians(N degrees) -> N { + return (degrees * std::numbers::pi_v<N>) / static_cast<N>(180); +} + +template <std::floating_point N> auto degrees(N radians) -> N { + return (radians * static_cast<N>(180)) / std::numbers::pi_v<N>; +} +} // 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<remove_reference_t<decltype(__VA_ARGS__)>&&>(__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<decltype(__VA_ARGS__)&&>(__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<typename T> + 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 <iostream> +#include <string_view> +#include <utility> +#include "format.hpp" + +namespace util { + +template <typename... Ts> +inline constexpr auto print(std::string_view fmt, Ts&&... args) + ->void{ + const auto str = util::format(fmt, std::forward<Ts>(args)...); + std::cout.write(str.c_str(), str.size()); +} + +template <typename... Ts> +inline constexpr auto println(std::string_view fmt, Ts&&... args) + ->void{ + print(fmt, std::forward<Ts>(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 <algorithm> +#include <ranges> +#include <unordered_set> +#include <vector> + + +namespace util { +namespace rg = std::ranges; +namespace detail { +template <typename Collection, rg::input_range R> +constexpr auto collect_range(R &&r) -> Collection { + Collection c; + if constexpr (rg::sized_range<R> && util::reservable<Collection>) { + c.reserve(r.size()); + } + + if constexpr (util::back_insertible<Collection>) { + rg::move(r, std::back_inserter(c)); + } else if constexpr (util::back_emplaceable<Collection, + decltype(*r.begin())>) { + for (auto &&e : r) { + c.emplace_back(std::move(e)); + } + } else if constexpr (util::emplaceable<Collection, decltype(*r.begin())>) { + for (auto &&e : r) { + c.emplace(std::move(e)); + } + } + + return c; +} + +template <typename Collection> struct collect_range_adaptor { + template <rg::input_range R> constexpr auto operator()(R &&r) const { + return collect_range<Collection>(std::forward<R>(r)); + } +}; + +template <typename Collection, rg::viewable_range R> +constexpr auto operator|(R &&r, + const collect_range_adaptor<Collection> &adaptor) { + return adaptor(std::forward<R>(r)); +} +} // namespace detail + +namespace views { + +template <typename Collection> +inline detail::collect_range_adaptor<Collection> collect; +} + +template <typename Collection, rg::range R> + +constexpr auto collect(R &&r) -> Collection { + return r | views::collect<Collection>; +} + +template <typename Range> +constexpr auto to_vector(Range r) + -> std::vector<rg::range_value_t<decltype(r)>> { + std::vector<rg::range_value_t<decltype(r)>> v; + + if constexpr (rg::sized_range<decltype(r)>) { + v.reserve(rg::size(r)); + } + + std::ranges::copy(r, std::back_inserter(v)); + + return v; +} + +template <typename Range> +constexpr auto to_unordered_set(Range r) + -> std::unordered_set<std::ranges::range_value_t<decltype(r)>> { + std::unordered_set<std::ranges::range_value_t<decltype(r)>> 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 <ranges> + +namespace util { +namespace rg = std::ranges; + +template <rg::sized_range R> +requires rg::view<R> +class drop_last_view : public rg::view_interface<drop_last_view<R>> { +private: + R base_{}; + std::iter_difference_t<rg::iterator_t<R>> count_{}; + rg::iterator_t<R> iter_{std::begin(base_)}; + +public: + drop_last_view() = default; + + constexpr drop_last_view(R base, + std::iter_difference_t<rg::iterator_t<R>> 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 R> { + const auto s = rg::size(base_); + const auto c = static_cast<decltype(s)>(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 <rg::viewable_range R> constexpr auto operator()(R &&r) const { + return drop_last_view(std::forward<R>(r), count_); + } +}; + +struct drop_last_range_adaptor { + template <rg::viewable_range R> + constexpr auto operator()(R &&r, + std::iter_difference_t<rg::iterator_t<R>> count) { + return drop_last_view(std::forward<R>(r), count); + } + + constexpr auto operator()(std::size_t count) { + return drop_last_range_adaptor_closure(count); + } +}; + +template <rg::viewable_range R> +constexpr auto operator|(R &&r, drop_last_range_adaptor_closure const &a) { + return a(std::forward<R>(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 <ranges> + + +namespace util { +namespace rg = std::ranges; + +template <rg::range R> struct enumerate_iterator_t : rg::iterator_t<R> { + using base = rg::iterator_t<R>; + using enumerator_type = std::size_t; + using value_type = std::pair<enumerator_type, rg::range_value_t<R>>; + using reference = + std::pair<enumerator_type, std::remove_cv_t<rg::range_reference_t<R>>>; + + 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<const base &>(*this) <=> + static_cast<const base &>(other); + } + + auto inc_enumerator() -> void { enumerator++; } + + auto operator++(int) -> enumerate_iterator_t { + const auto result = static_cast<base &>(*this)++; + inc_enumerator(); + return result; + } + + auto operator++() -> enumerate_iterator_t & { + inc_enumerator(); + ++static_cast<base &>(*this); + return (*this); + } + + auto operator*() -> reference { + return reference(enumerator, *static_cast<base &>(*this)); + } + auto operator*() const -> reference { + return reference(enumerator, *static_cast<const base &>(*this)); + } +}; + +template <rg::range R> +class enumerate_view : public rg::view_interface<enumerate_view<R>> { + R base_{}; + isize enumerator{}; + enumerate_iterator_t<R> 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 <rg::viewable_range R> constexpr auto operator()(R &&r) const { + return enumerate_view(std::forward<R>(r)); + } +}; + +struct enumerate_range_adaptor { + template <rg::viewable_range R> constexpr auto operator()(R &&r) { + return enumerate_view(std::forward<R>(r)); + } + + constexpr auto operator()() { return enumerate_range_adaptor_closure(); } +}; + +template <rg::viewable_range R> +constexpr auto operator|(R &&r, enumerate_range_adaptor_closure const &a) { + return a(std::forward<R>(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 <optional> +#include <ranges> +#include <xutility> + + +namespace util { +namespace rg = std::ranges; +namespace detail { + +template <rg::input_range R> +constexpr auto get_first(R &&r) -> std::optional<rg::range_value_t<R>> { + if (rg::begin(r) == rg::end(r)) { + return std::nullopt; + } else { + return *rg::begin(r); + } +} + +struct first_range_adaptor { + template <rg::input_range R> constexpr auto operator()(R &&r) const { + return get_first(std::forward<R>(r)); + } +}; + +template <rg::viewable_range R> +constexpr auto operator|(R &&r, const first_range_adaptor &adaptor) { + return adaptor(std::forward<R>(r)); +} +} // namespace detail + +namespace views { + +inline detail::first_range_adaptor first; +} + +template <rg::range R> +constexpr auto get_first(R &&r) -> std::optional<rg::range_value_t<R>>{ + 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 <concepts> +#include <cstddef> +#include <ranges> +#include <xutility> + + +namespace util { +namespace rg = std::ranges; + +namespace detail { + +template <std::invocable F> struct generated_iterator { + using value_type = decltype(std::declval<F>()()); + 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 <std::invocable F> +class generated_view : rg::view_interface<generated_view<F>> { + 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 <std::invocable F> constexpr auto operator()(F &&fn) const { + return generated_view(std::forward<F>(fn)); + } +}; + +struct generated_range_adaptor { + template <std::invocable F> constexpr auto operator()(F &&r) { + return generated_view(std::forward<F>(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 <optional> + +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 <chrono> +#include <functional> +#include <type_traits> +#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<Return, duration_type>; + using step_type = std::function<step_return_type()>; + + std::vector<step_type> 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 <cctype> +#include <limits> +#include <string_view> +#include <type_traits> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#endif + +#include <algorithm> +#include <locale> +#include <sstream> +#include <string> +#include <vector> + +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 <typename A> +std::basic_string<A> to_lower(std::basic_string_view<A> view) { + std::basic_string<A> out(view.size(), '\0'); + + std::transform( + std::begin(view), std::end(view), std::begin(out), + [](typename std::basic_string<A>::value_type c) { + return std::use_facet< + std::ctype<typename std::basic_string<A>::value_type>>( + std::locale()) + .tolower(c); + }); + + return out; +} + +template <typename CharT> +constexpr bool string_eq_nocase(std::basic_string_view<CharT> rh, + std::basic_string_view<CharT> lh) { + const auto rh_lower = to_lower<CharT>(rh); + const auto lh_lower = to_lower<CharT>(lh); + + return rh_lower == lh_lower; +} + +template <typename CharT> +auto split_with(const std::basic_string<CharT> &str, CharT delimiter) + -> std::vector<std::basic_string<CharT>> { + std::vector<std::basic_string<CharT>> parts; + std::basic_string<CharT> part; + + const auto start = str.find_first_not_of(delimiter); + const auto end = str.find_last_not_of(delimiter); + std::basic_istringstream<CharT> stream( + str.substr(start, end == std::string::npos ? end : end + 1)); + + while (std::getline(stream, part, delimiter)) + parts.push_back(part); + + return parts; +} + +#include <concepts> + +template <std::integral I> +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<I>::max(); +} + +template <std::integral I> +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<I>::max(); + + for (auto &&c : view) { + const auto i = from_char_radix<I>(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<I>) { + 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 <memory> +#include <optional> +#include <concepts> +#include <type_traits> +#include <vector> + +namespace util { +namespace detail { +template <int N, typename... Ts> +using nth_type = typename std::tuple_element<N, std::tuple<Ts...>>::type; + +template <typename> consteval std::size_t locate(std::size_t ind) { + return static_cast<std::size_t>(-1); +} + +template <typename IndexedType, typename T, typename... Ts> +consteval std::size_t locate(std::size_t ind = 0) { + if (std::is_same<IndexedType, T>::value) { + return ind; + } else { + return locate<IndexedType, Ts...>(ind + 1); + } +} + +template <typename T, typename... Ts> +struct index_of + : std::integral_constant<std::size_t, locate<T, std::tuple<Ts...>>> {}; + +template <typename T, typename... Ts> + inline constexpr std::size_t index_of_v = index_of<T, Ts...>::value; + + +template <typename IndexedType, typename...> struct is_in : std::false_type {}; + +template <typename IndexedType, typename Item, typename... Items> +struct is_in<IndexedType, Item, Items...> + : std::integral_constant<bool, std::is_same<IndexedType, Item>::value || + is_in<IndexedType, Items...>::value> {}; + + +template <typename Item, typename... Items> +concept in_types = is_in<std::size_t, Item, Items...>::value; + + +template<typename T> +inline constexpr auto max_all_inner(T arg) -> T { + return arg; +} + +template<typename T, typename... Ts> +inline constexpr auto max_all_inner(T arg, Ts... args) -> decltype(max_all_inner(arg)){ + return std::max(arg, max_all_inner(args...)); +} + +template<typename... Ts> +inline constexpr auto max_all(Ts... args) -> usize { + return max_all_inner(args...); +} + +template <typename... Ts> class variadic_union { + std::vector<u8> m_data; + std::optional<usize> m_type_index; + +public: + variadic_union() : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(std::nullopt) {} + + template<std::size_t I, typename... Us> + variadic_union(Us&&... args) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) { + *reinterpret_cast<detail::nth_type<I, Ts...>*>(m_data.data()) = detail::nth_type<I, Ts...>{std::forward<Us>(args)...}; + } + + template<std::size_t I, typename U> + requires (std::same_as<U, detail::nth_type<I, Ts...>>) + variadic_union(const U& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) { + *reinterpret_cast<detail::nth_type<I, Ts...>*>(m_data.data()) = value; + } + + template<std::size_t I, typename U> + requires (std::same_as<U, detail::nth_type<I, Ts...>>) + variadic_union(U&& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) { + *reinterpret_cast<detail::nth_type<I, Ts...>*>(m_data.data()) = std::move(value); + } + + template<typename U> + requires (in_types<U, Ts...>) + variadic_union(U&& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(index_of_v<U, Ts...>) { + *reinterpret_cast<U*>(m_data.data()) = std::move(value); + } + + template<typename U> + requires (in_types<U, Ts...>) + variadic_union(const U& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(index_of_v<U, Ts...>) { + *reinterpret_cast<U*>(m_data.data()) = value; + } + +template<typename U> +constexpr auto get() -> U& { + util::assert_eq(m_type_index.value(), index_of_v<U, Ts...>); + + return reinterpret_cast<U*>(m_data.data()); +} +}; + +} // namespace detail + +#define NTH(Is, Ts) typename detail::nth_type<, Ts...> element_##I; + +template <util::enum_type Tag, typename... Ts> class TaggedUnion { + Tag tag; + detail::variadic_union<Ts...> inner; +public: +template <typename U> + requires (detail::in_types<U, Ts...>) +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 <algorithm> +#include <optional> +#include <string_view> +#include <vector> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <Windows.h> + +#include <TlHelp32.h> + +namespace util { +namespace threads { + +auto get_all_threads(u32 pid) -> std::vector<u32>; + +auto get_all_threads() -> std::vector<u32>; +auto get_other_threads() -> std::vector<u32>; + +auto suspend_threads(const std::vector<u32> &thread_ids) -> std::vector<u32>; + +auto resume_threads(const std::vector<u32> &thread_ids) -> bool; + +/// suspends all threads, only then executes function, and resumes threads +/// returns `nullopt` if not all threads were suspended. +template <typename T, typename F> +auto with_suspended_threads(F &&fn) -> std::optional<T> { + auto threads = get_other_threads(); + + auto suspended_threads = suspend_threads(threads); + + const auto suspended_all_threads = suspended_threads.size() == threads.size(); + + std::optional<T> 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<u32>; +auto get_process_id(std::wstring_view name) -> std::optional<u32>; +} // namespace processes + +/// impl + +namespace threads { +inline auto get_all_threads(u32 pid) -> std::vector<u32> { + std::vector<u32> 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<u32> { + return get_all_threads(GetCurrentProcessId()); +} + +inline auto get_other_threads() -> std::vector<u32> { + 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<u32> &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<u32> &thread_ids) + -> std::vector<u32> { + std::vector<u32> 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<u32> { + auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + std::optional<u32> 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<u32> { + auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + std::optional<u32> 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 <chrono> +#include <ratio> + +namespace util { +using std::chrono::system_clock; +using namespace std::chrono_literals; + +template <typename F> +auto time_of(F &&fn) -> std::chrono::duration<f64, std::milli> { + 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 <typename Rep, typename Ratio> + inline auto has_elapsed(const std::chrono::duration<Rep, Ratio> &duration) + -> bool { + const auto now = system_clock::now(); + + if (duration < (now - last)) { + last = now; + return true; + } else { + return false; + } + } + + template <typename Rep = usize, typename Ratio = std::milli> + inline auto elapsed() -> std::chrono::duration<Rep, Ratio> { + const auto now = system_clock::now(); + const auto duration = + std::chrono::duration_cast<std::chrono::duration<Rep, Ratio>>(now - + last); + last = now; + + return duration; + } + + inline auto has_elapsed_s(f64 seconds) -> bool { + return has_elapsed(std::chrono::duration<f64>(seconds)); + } + + inline auto has_elapsed_ms(i64 milliseconds) -> bool { + return has_elapsed(std::chrono::duration<i64, std::milli>(milliseconds)); + } + + inline auto elapsed_ms() -> std::chrono::duration<isize, std::milli> { + const auto now = system_clock::now(); + const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( + system_clock::now() - last); + last = now; + return duration; + } + + inline auto elapsed_s() -> std::chrono::duration<f64> { + const auto now = system_clock::now(); + const auto duration = + std::chrono::duration<f64>(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 <typename T, T V> +struct integral_constant {static constexpr T value = V;}; + +struct true_type : integral_constant<bool, true> {}; +struct false_type : integral_constant<bool, false> {}; + +inline constexpr bool true_type_v = true_type::value; +inline constexpr bool false_type_v = false_type::value; + +namespace detail { +template <class T> struct type_identity { + using type = T; +}; // or use std::type_identity (since C++20) + +template <class T> // Note that `cv void&` is a substitution failure +auto try_add_lvalue_reference(int) -> type_identity<T &>; +template <class T> // Handle T = cv void case +auto try_add_lvalue_reference(...) -> type_identity<T>; + +template <class T> auto try_add_rvalue_reference(int) -> type_identity<T &&>; +template <class T> auto try_add_rvalue_reference(...) -> type_identity<T>; + +} // namespace detail + +template <class T> +struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) { +}; + +template <class T> +struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) { +}; + +template <class T> +using add_rvalue_reference_t = typename add_rvalue_reference<T>::type; + +template <class T> add_rvalue_reference_t<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 <class T> +auto test_returnable(int) + -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{}); +template <class> auto test_returnable(...) -> false_type; + +template <class From, class To> +auto test_implicitly_convertible(int) + -> decltype(void(declval<void (&)(To)>()(declval<From>())), + true_type{}); +template <class, class> +auto test_implicitly_convertible(...) -> false_type; + +} // namespace detail + +template <typename T> +struct is_void : false_type{}; + +template<> +struct is_void<void> : true_type {}; + +template <typename T> +inline constexpr bool is_void_v = is_void<T>::value; + +template< class T > struct remove_reference { typedef T type; }; +template< class T > struct remove_reference<T&> { typedef T type; }; +template< class T > struct remove_reference<T&&> { typedef T type; }; + +template< class T > +using remove_reference_t = typename remove_reference<T>::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 <cstddef> +#include <cstdint> + +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 <cstdlib> +#include <iostream> +#include <stdio.h> + +#define UTIL_ASSERT_FORMAT +#include "assert.hpp" +#include "ranges/collect.hpp" +#include <ranges> +#include <set> +#include <vector> +#include <string_view> + +using namespace std::string_view_literals; + +int main(int argc, const char *argv[]) { + printf("Hello, Alloy!\n"); + auto vec = std::vector<int>({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<std::string>; + +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<std::set<int>>(range); + return 1; +} \ No newline at end of file