initial commit

This commit is contained in:
janis 2022-06-23 17:26:19 +01:00
commit 62272807dc
27 changed files with 1747 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
compile_commands.json
.cache/
target/

16
Alloy.toml Normal file
View file

@ -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"

120
include/assert.hpp Normal file
View file

@ -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

View file

@ -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

View file

@ -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

158
include/enum.hpp Normal file
View file

@ -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

50
include/format.hpp Normal file
View file

@ -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;
}
};

35
include/iterator.hpp Normal file
View file

@ -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

16
include/math.hpp Normal file
View file

@ -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

12
include/move.hpp Normal file
View file

@ -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__)

13
include/option.hpp Normal file
View file

@ -0,0 +1,13 @@
#pragma once
namespace util {
template<typename T>
class Option {
public:
private:
bool m_has_value;
T m_value;
};
}

24
include/print.hpp Normal file
View file

@ -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');
}
}

View file

@ -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

View file

@ -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

View file

@ -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

42
include/ranges/first.hpp Normal file
View file

@ -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

View file

@ -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

10
include/result.hpp Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include <optional>
namespace util {
class Result {};
using result = Result;
using expected = Result;
} // namespace util

View file

@ -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 = "";
};
}

101
include/step_function.hpp Normal file
View file

@ -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;
}
}

151
include/string.hpp Normal file
View file

@ -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

124
include/tagged_union.hpp Normal file
View file

@ -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

164
include/thread.hpp Normal file
View file

@ -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

74
include/time.hpp Normal file
View file

@ -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

78
include/type_traits.hpp Normal file
View file

@ -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;
}

24
include/types.hpp Normal file
View file

@ -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;

50
src/main.cc Normal file
View file

@ -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;
}