Last active
May 18, 2022 11:18
-
-
Save Atari2/5bc5f83670b1fab7baf8c5db9172c52f to your computer and use it in GitHub Desktop.
Common utilities in a header file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <array> | |
#include <bit> | |
#include <concepts> | |
#include <cstdint> | |
#include <optional> | |
#include <stdexcept> | |
#include <string> | |
#include <string_view> | |
#ifdef BACKTRACE_AVAILABLE | |
#include "BackTrace.h" | |
#endif | |
// common and useful macros / functions | |
#define STRIMPL(x) #x | |
#define STR(x) STRIMPL(x) | |
// defines a literal with the format "DEBUG [file:line]: <info>" | |
#define DEBUGINFO(info) "DEBUG [" __FILE__ ":" STR(__LINE__) "]: " STR(info) | |
#define WIDEIMPL(str) L##str | |
#define WIDE(str) WIDEIMPL(str) | |
// A catch-all tag type. | |
template <typename T> | |
struct TagType {}; | |
// Concept for enum template parameters | |
template <typename T> | |
concept EnumT = std::is_enum_v<T>; | |
template <typename T, size_t N> | |
constexpr size_t sizeof_array(const T (&)[N]) { | |
return N; | |
} | |
// Simple choice support | |
template <typename T> | |
constexpr auto choice(bool t, T ret_true, T ret_false) { | |
return (t ? ret_true : ret_false); | |
} | |
template <typename T> | |
constexpr auto triple_choice(bool a, bool b, T reta, T retb, T retc) { | |
return (a ? reta : (b ? retb : retc)); | |
} | |
// enum conversions | |
template <EnumT T> | |
constexpr auto from_enum(T val) { | |
return static_cast<std::underlying_type_t<T>>(val); | |
} | |
template <EnumT T> | |
constexpr auto to_enum(std::integral auto val) { | |
return static_cast<T>(static_cast<std::underlying_type_t<T>>(val)); | |
} | |
// an always_false template dependant bool for static_asserts in if constexpr code | |
template <typename T> | |
constexpr inline bool always_false_v = false; | |
// size explicit integer shorthands and operators | |
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 c = char; | |
using uc = unsigned char; | |
constexpr u8 operator""_u8(unsigned long long val) { | |
return static_cast<u8>(val); | |
} | |
constexpr u16 operator""_u16(unsigned long long val) { | |
return static_cast<u16>(val); | |
} | |
constexpr u32 operator""_u32(unsigned long long val) { | |
return static_cast<u32>(val); | |
} | |
constexpr u64 operator""_u64(unsigned long long val) { | |
return static_cast<u64>(val); | |
} | |
constexpr i8 operator""_i8(unsigned long long val) { | |
return static_cast<i8>(val); | |
} | |
constexpr i16 operator""_i16(unsigned long long val) { | |
return static_cast<i16>(val); | |
} | |
constexpr i32 operator""_i32(unsigned long long val) { | |
return static_cast<i32>(val); | |
} | |
constexpr i64 operator""_i64(unsigned long long val) { | |
return static_cast<i64>(val); | |
} | |
constexpr size_t operator""_sz(unsigned long long val) { | |
return static_cast<size_t>(val); | |
} | |
constexpr intptr_t operator""_iptr(unsigned long long val) { | |
return static_cast<intptr_t>(val); | |
} | |
constexpr uintptr_t operator""_uptr(unsigned long long val) { | |
return static_cast<uintptr_t>(val); | |
} | |
constexpr c operator""_c(char val) { | |
return static_cast<c>(val); | |
} | |
constexpr uc operator""_uc(char val) { | |
return static_cast<uc>(val); | |
} | |
// platform information | |
enum class PlatformType { Windows = 0, Apple = 1, Unix = 2 }; | |
enum class PlatformWidth { x64 = 0, x86 = 1 }; | |
namespace __internal { | |
template <PlatformType TType, PlatformWidth TWidth> | |
struct PlatformInfoImpl { | |
static_assert( | |
TType == PlatformType::Windows || TType == PlatformType::Apple || TType == PlatformType::Unix, | |
"Invalid platform type" | |
); | |
static_assert(TWidth == PlatformWidth::x64 || TWidth == PlatformWidth::x86, "Invalid platform width"); | |
constexpr static PlatformType Type = TType; | |
constexpr static bool WINDOWS = (TType == PlatformType::Windows); | |
constexpr static bool UNIX = (TType == PlatformType::Unix); | |
constexpr static bool APPLE = (TType == PlatformType::Apple); | |
constexpr static PlatformWidth Width = TWidth; | |
constexpr static bool X64 = (TWidth == PlatformWidth::x64); | |
constexpr static bool X86 = (TWidth == PlatformWidth::x86); | |
constexpr static const char* Name = triple_choice(WINDOWS, UNIX, "Windows", "Unix", "Apple"); | |
constexpr static const char* StrWidth = choice(X64, "x64", "x86"); | |
constexpr static auto type() { return Type; } | |
constexpr static bool is_windows() { return WINDOWS; } | |
constexpr static bool is_unix() { return UNIX; } | |
constexpr static bool is_apple() { return APPLE; } | |
template <typename T = PlatformWidth> | |
constexpr static auto width() { | |
if constexpr (std::same_as<T, PlatformWidth>) { | |
return Width; | |
} else if constexpr (std::same_as<T, char*>) { | |
return StrWidth; | |
} else { | |
static_assert( | |
always_false_v<T>, "width() call is only valid with PlatformWidth or char* as template parameters" | |
); | |
return nullptr; | |
} | |
} | |
constexpr static bool x64() { return X64; } | |
constexpr static bool x86() { return X86; } | |
constexpr static auto name() { return Name; } | |
}; | |
} // namespace __internal | |
#ifdef _WIN32 | |
// Windows | |
#ifdef _WIN64 | |
using PlatformInfo = __internal::PlatformInfoImpl<PlatformType::Windows, PlatformWidth::x64>; | |
#else | |
using PlatformInfo = __internal::PlatformInfoImpl<PlatformType::Windows, PlatformWidth::x86>; | |
#endif | |
#define unreachable __assume(0) | |
#define forceinline __forceinline | |
#define noop __noop | |
#define NEWLINE "\r\n" | |
#elif __APPLE__ | |
// Apple | |
#if defined(__LP64__) || defined(__x86_64__) | |
using PlatformInfo = __internal::PlatformInfoImpl<PlatformType::Apple, PlatformWidth::x64>; | |
#else | |
using PlatformInfo = __internal::PlatformInfoImpl<PlatformType::Apple, PlatformWidth::x86>; | |
#endif | |
#define unreachable __builtin_unreachable(); | |
#define forceinline __attribute__((always_inline)) | |
#define noop ((void)0) | |
#define NEWLINE "\n" | |
#elif __unix__ | |
// Unix | |
#if defined(__LP64__) || defined(__x86_64__) | |
using PlatformInfo = __internal::PlatformInfoImpl<PlatformType::Unix, PlatformWidth::x64>; | |
#else | |
using PlatformInfo = __internal::PlatformInfoImpl<PlatformType::Unix, PlatformWidth::x86>; | |
#endif | |
#define unreachable __builtin_unreachable(); | |
#define forceinline __attribute__((always_inline)) | |
#define noop ((void)0) | |
#define NEWLINE "\n" | |
#else | |
#error "Unknown platform" | |
#endif | |
/* Constexpr support for various utilities */ | |
namespace cxpr { | |
constexpr size_t strlen(const char* ptr) { | |
if (!ptr) return 0; | |
size_t len = 0; | |
while (*(ptr++)) len++; | |
return len; | |
} | |
constexpr bool isspace(const unsigned char c) { | |
constexpr const std::array whitespaces{ '\t'_uc, '\n'_uc, '\v'_uc, '\f'_uc, '\r'_uc }; | |
constexpr unsigned char begin_ws_range = whitespaces.front(); | |
constexpr unsigned char end_ws_range = whitespaces.back(); | |
return c == ' ' || (c >= begin_ws_range && c <= end_ws_range); | |
} | |
constexpr bool strncmp(const char* lhs, const char* rhs, size_t size) { | |
for (size_t i = 0; i < size; i++) { | |
if (lhs[i] != rhs[i]) return false; | |
} | |
return true; | |
} | |
constexpr size_t min(size_t lhs, size_t rhs) { | |
return (lhs < rhs) ? lhs : rhs; | |
} | |
// minimal string_view constexpr implementation | |
// this exists only because std::string_view is constexpr-capable only on msvc | |
// (as of 20 april 2022) | |
class string_view { | |
const char* m_ptr = nullptr; | |
size_t m_size = 0; | |
public: | |
constexpr static size_t npos = static_cast<size_t>(-1); | |
constexpr string_view(const char* ptr, size_t size) : m_ptr(ptr), m_size(size) {} | |
constexpr string_view(const char* ptr) : m_ptr(ptr), m_size(strlen(ptr)) {} | |
constexpr string_view() = default; | |
constexpr const char* data() const { return m_ptr; } | |
constexpr size_t size() const { return m_size; } | |
constexpr size_t length() const { return m_size; } | |
constexpr char operator[](size_t index) const { return m_ptr[index]; } | |
constexpr bool operator==(const char* ptr) const { | |
if (strlen(ptr) == m_size) return strncmp(ptr, m_ptr, m_size); | |
return false; | |
} | |
constexpr bool operator==(const string_view other) const { | |
if (other.size() == m_size) return strncmp(other.data(), m_ptr, m_size); | |
return false; | |
} | |
constexpr bool operator!=(const char* ptr) const { return !(*this == ptr); } | |
constexpr bool operator!=(const string_view other) const { return !(*this == other); } | |
constexpr const char* begin() const { return m_ptr; } | |
constexpr const char* end() const { return m_ptr + m_size; } | |
constexpr string_view substr(size_t pos = 0, size_t count = npos) const { | |
const size_t rcount = min(count, size() - pos); | |
return string_view{ m_ptr + pos, rcount }; | |
} | |
constexpr bool empty() const { return m_size == 0; } | |
constexpr size_t find_first_of(char c, size_t off = 0) const { | |
if (off > m_size) return npos; | |
for (size_t i = off; i < m_size; i++) { | |
if (m_ptr[i] == c) return i; | |
} | |
return npos; | |
} | |
constexpr size_t find_first_not_of(char c, size_t off = 0) const { | |
if (off > m_size) return npos; | |
for (size_t i = off; i < m_size; i++) { | |
if (m_ptr[i] != c) return i; | |
} | |
return npos; | |
} | |
explicit operator std::string() const { return std::string{ m_ptr, m_size }; } | |
constexpr operator std::string_view() const { return std::string_view{ m_ptr, m_size }; } | |
friend std::ostream& operator<<(std::ostream& stream, const string_view view) { | |
return stream << std::string_view{ view.data(), view.size() }; | |
} | |
}; | |
} // namespace cxpr | |
#if _MSC_VER | |
using _string_view = std::string_view; | |
#else | |
using _string_view = cxpr::string_view; | |
#endif | |
// constexpr string->integer conversion | |
namespace __internal { | |
template <bool Signed = true> | |
constexpr auto StrViewTo64Trimmed(const _string_view str) { | |
constexpr std::array values_table_i64 = { 1_i64, | |
10_i64, | |
100_i64, | |
1000_i64, | |
10000_i64, | |
100000_i64, | |
1000000_i64, | |
10000000_i64, | |
100000000_i64, | |
1000000000_i64, | |
10000000000_i64, | |
100000000000_i64, | |
1000000000000_i64, | |
10000000000000_i64, | |
100000000000000_i64, | |
1000000000000000_i64, | |
10000000000000000_i64, | |
100000000000000000_i64, | |
1000000000000000000_i64 }; | |
constexpr std::array value_table_u64 = { 1_u64, | |
10_u64, | |
100_u64, | |
1000_u64, | |
10000_u64, | |
100000_u64, | |
1000000_u64, | |
10000000_u64, | |
100000000_u64, | |
1000000000_u64, | |
10000000000_u64, | |
100000000000_u64, | |
1000000000000_u64, | |
10000000000000_u64, | |
100000000000000_u64, | |
1000000000000000_u64, | |
10000000000000000_u64, | |
100000000000000000_u64, | |
1000000000000000000_u64, | |
10000000000000000000_u64 }; | |
using RetType = std::conditional_t<Signed, int64_t, uint64_t>; | |
size_t start_idx = 0; | |
size_t end_idx = str.size(); | |
bool neg = false; | |
if constexpr (Signed) { | |
char maybe_sign = str[0]; | |
neg = maybe_sign == '-'; | |
start_idx += (maybe_sign == '-' || maybe_sign == '+'); | |
} | |
RetType result = 0; | |
// skip leading zeros | |
while (start_idx < end_idx) { | |
if (str[start_idx] != '0') break; | |
start_idx++; | |
} | |
if (start_idx == end_idx) return RetType{ 0 }; | |
constexpr size_t max_uint64_size = cxpr::strlen("18446744073709551615"); | |
constexpr size_t max_int64_size = cxpr::strlen("9223372036854775807"); | |
constexpr auto max_size = Signed ? max_int64_size : max_uint64_size; | |
if (end_idx - start_idx > max_size) return RetType{ 0 }; | |
constexpr auto get_values_table = [values_table_i64, value_table_u64]() { | |
if constexpr (Signed) | |
return values_table_i64; | |
else | |
return value_table_u64; | |
}; | |
constexpr const auto values_table = get_values_table(); | |
// 0-9 => 48-57 | |
for (size_t idx = end_idx - 1, tbl_idx = 0; idx >= start_idx; idx--, tbl_idx++) { | |
result += static_cast<RetType>(str[idx] - '0') * values_table[tbl_idx]; | |
if (idx == 0) break; | |
} | |
if constexpr (Signed) | |
return result * (neg ? -1 : 1); | |
else | |
return result; | |
} | |
} // namespace __internal | |
template <bool Signed> | |
constexpr int64_t StrViewTo64(const _string_view view) { | |
size_t cur_index = 0; | |
size_t max_index = view.length(); | |
// skip leading and trailing whitespace | |
if (max_index == 0) return 0; | |
while (cxpr::isspace(view[cur_index])) { | |
cur_index++; | |
if (cur_index == max_index) return 0; | |
} | |
while (cxpr::isspace(view[max_index - 1])) { | |
max_index--; | |
if (max_index == _string_view::npos) return 0; | |
} | |
if (cur_index == max_index) return 0; | |
return __internal::StrViewTo64Trimmed<Signed>(view.substr(cur_index, max_index)); | |
} | |
constexpr inline auto StrViewToU64 = StrViewTo64<false>; | |
constexpr inline auto StrViewToI64 = StrViewTo64<true>; | |
namespace EnumHelpers { | |
template <EnumT V, size_t N> | |
class EnumStrHashMap { | |
// round up to power of 2 to avoid modulos and just use bitwise ops | |
constexpr static size_t ACTUAL_SIZE = std::bit_ceil(N); | |
constexpr static size_t FOR_MOD_OPS = ACTUAL_SIZE - 1; | |
struct KeyBkt { | |
V key; | |
bool used; | |
}; | |
std::array<std::pair<KeyBkt, _string_view>, ACTUAL_SIZE> m_internal_map; | |
size_t m_size = 0; | |
public: | |
constexpr void insert(V key, _string_view val) { | |
if (m_size == m_internal_map.size()) throw std::runtime_error("This map is full"); | |
size_t idx = from_enum(key) & FOR_MOD_OPS; | |
while (m_internal_map[idx].first.used) { idx = (idx + 1) & FOR_MOD_OPS; } | |
m_internal_map[idx] = std::make_pair(KeyBkt{ std::move(key), true }, val); | |
m_size++; | |
} | |
constexpr _string_view operator[](V key) const { | |
size_t idx = from_enum(key) & FOR_MOD_OPS; | |
size_t n_tries = 0; | |
while (m_internal_map[idx].first.key != key) { | |
idx = (idx + 1) & FOR_MOD_OPS; | |
if (n_tries++ == m_size) throw std::runtime_error("This enum value is not valid"); | |
} | |
return m_internal_map[idx].second; | |
} | |
}; | |
constexpr size_t count_enum_values(_string_view view) { | |
size_t count = 1; | |
for (char c : view) count += (c == ','); | |
return count; | |
} | |
template <EnumT T> | |
constexpr _string_view get_enum_full_string(TagType<T>) { | |
return ""; | |
} | |
template <EnumT T> | |
requires(!get_enum_full_string<T>({}).empty()) constexpr auto construct_enum_array(TagType<T>) { | |
constexpr _string_view view = get_enum_full_string<T>({}); | |
std::array<std::pair<_string_view, T>, count_enum_values(view)> enum_map_l; | |
std::underlying_type_t<T> current_val{}; | |
auto get_pair = [&view, ¤t_val](size_t first_idx, size_t second_idx) { | |
constexpr bool Signed = std::is_signed_v<std::underlying_type_t<T>>; | |
auto sub = view.substr(first_idx, second_idx - first_idx); | |
auto equal_idx = sub.find_first_of('='); | |
if (equal_idx == std::string_view::npos) { | |
// no equal, get next value | |
auto first_nospace_idx = sub.find_first_not_of(' '); | |
auto last_nospace_idx = sub.find_first_of(' ', first_nospace_idx); | |
return std::make_pair(sub.substr(first_nospace_idx, last_nospace_idx), to_enum<T>(current_val++)); | |
} else { | |
auto first_nospace_idx = sub.find_first_not_of(' '); | |
auto last_nospace_idx = sub.find_first_of(' ', first_nospace_idx); | |
return std::make_pair( | |
sub.substr( | |
first_nospace_idx, (last_nospace_idx < equal_idx ? last_nospace_idx : equal_idx) - first_nospace_idx | |
), | |
to_enum<T>(StrViewTo64<Signed>(sub.substr(equal_idx + 1))) | |
); | |
} | |
}; | |
size_t first_idx = 0; | |
size_t second_idx = view.find_first_of(','); | |
size_t i = 0; | |
while (second_idx != _string_view::npos) { | |
enum_map_l[i++] = get_pair(first_idx, second_idx); | |
first_idx = second_idx + 1; | |
second_idx = view.find_first_of(',', first_idx); | |
} | |
enum_map_l[i] = get_pair(first_idx, view.size()); | |
return enum_map_l; | |
} | |
template <EnumT T> | |
constexpr auto construct_enum_map( | |
const std::array<std::pair<_string_view, T>, count_enum_values(get_enum_full_string<T>({}))>& enum_array | |
) { | |
constexpr size_t sz = count_enum_values(get_enum_full_string<T>({})); | |
EnumStrHashMap<T, sz> map; | |
for (const auto& [k, v] : enum_array) { map.insert(v, k); } | |
return map; | |
} | |
template <EnumT T> | |
requires(requires { construct_enum_array<T>({}); }) struct EnumMapProvider { | |
constexpr static auto enum_array = construct_enum_array<T>({}); | |
constexpr static auto map = construct_enum_map<T>(enum_array); | |
}; | |
template <typename T> | |
concept EnumSupportsMap = EnumT<T> && requires { | |
{ EnumMapProvider<T>{} }; | |
}; | |
template <EnumSupportsMap T> | |
class EnumIterator { | |
size_t index = 0; | |
constexpr static const auto& s_enum_map = EnumMapProvider<T>::enum_array; | |
public: | |
using iterator_category = std::input_iterator_tag; | |
using value_type = T; | |
using reference = T&; | |
constexpr EnumIterator() = default; | |
constexpr EnumIterator(size_t idx) : index(idx) {} | |
constexpr const T& operator*() const { return s_enum_map[index].second; } | |
constexpr const T& operator->() const { return s_enum_map[index].second; } | |
constexpr bool operator==(const EnumIterator& other) const { return index == other.index; } | |
constexpr bool operator!=(const EnumIterator& other) const { return index != other.index; } | |
constexpr EnumIterator& operator++() { | |
index++; | |
return *this; | |
} | |
constexpr EnumIterator operator++(int) { | |
EnumIterator iter = *this; | |
index++; | |
return iter; | |
} | |
constexpr EnumIterator& operator--() { | |
index--; | |
return *this; | |
} | |
constexpr EnumIterator operator--(int) { | |
EnumIterator iter = *this; | |
index--; | |
return iter; | |
} | |
}; | |
} // namespace EnumHelpers | |
template <EnumT E> | |
class std::iterator_traits<EnumHelpers::EnumIterator<E>> { | |
public: | |
using Iter = EnumHelpers::EnumIterator<E>; | |
using difference_type = void; | |
using value_type = E; | |
using pointer = void; | |
using reference = E&; | |
using iterator_category = std::input_iterator_tag; | |
}; | |
enum class EnumStrSearchType { HashMap, Linear }; | |
template <EnumStrSearchType SearchType = EnumStrSearchType::HashMap, EnumHelpers::EnumSupportsMap T> | |
constexpr _string_view enum_to_str_view(T e) { | |
if constexpr (SearchType == EnumStrSearchType::Linear) { | |
constexpr const auto& enum_map = EnumHelpers::EnumMapProvider<T>::enum_array; | |
auto it = std::find_if(enum_map.begin(), enum_map.end(), [&e](const auto& p) { | |
const auto& [name, val] = p; | |
return val == e; | |
}); | |
if (it != enum_map.end()) return it->first; | |
if (std::is_constant_evaluated()) { | |
// this allocation is here to stop compilation | |
// if your code reached this allocation it means you passed an invalid value to enum_to_str() | |
// this *does not* cause leaks because it only gets executed during constexpr evaluation | |
// and during constexpr evaluation allocations which are not freed in the same scope cause a compilation | |
// failure because the expression is not constant anymore for example, the error msvc gives you is: | |
// `error C2131: expression did not evaluate to a constant` | |
// `Common.h(341): note: failure was caused by allocated storage not being deallocated` | |
new int[from_enum(e)]; | |
} | |
return "invalid enum value"; | |
} else { | |
// I'm not sure if this is better than linear search, it's really just a poor's man linear probing | |
// hash map with O(1) access time (if no collisions) | |
// and O(N) max round trip. It will throw if the enum isn't in the map | |
constexpr const auto& enum_map = EnumHelpers::EnumMapProvider<T>::map; | |
return enum_map[e]; | |
} | |
} | |
template <EnumHelpers::EnumSupportsMap T> | |
constexpr size_t enum_size() { | |
constexpr const auto& enum_array = EnumHelpers::EnumMapProvider<T>::enum_array; | |
return enum_array.size(); | |
} | |
template <EnumStrSearchType SearchType = EnumStrSearchType::HashMap, EnumHelpers::EnumSupportsMap T> | |
std::string enum_to_str(T e) { | |
return std::string{ enum_to_str_view<SearchType, T>(e) }; | |
} | |
template <EnumHelpers::EnumSupportsMap T> | |
struct ForEachEnum { | |
constexpr auto begin() const { return EnumHelpers::EnumIterator<T>{ 0 }; } | |
constexpr auto end() const { | |
return EnumHelpers::EnumIterator<T>{ EnumHelpers::EnumMapProvider<T>::enum_array.size() }; | |
} | |
}; | |
template <EnumHelpers::EnumSupportsMap T> | |
constexpr auto for_each_enum() { | |
return ForEachEnum<T>{}; | |
} | |
template <EnumHelpers::EnumSupportsMap T, typename Functor> | |
requires(requires { std::declval<Functor>()(std::declval<T>()); }) constexpr auto for_each_enum(Functor func) { | |
using Res = std::invoke_result_t<Functor, T>; | |
if constexpr (std::is_void_v<Res>) { | |
for (auto val : ForEachEnum<T>{}) { func(val); } | |
} else { | |
std::array<Res, EnumHelpers::EnumMapProvider<T>::enum_array.size()> result_array; | |
size_t i = 0; | |
for (auto val : ForEachEnum<T>{}) { result_array[i++] = func(val); } | |
return result_array; | |
} | |
} | |
#define ENUM_TO_STR(en, ...) \ | |
namespace EnumHelpers { \ | |
template <> \ | |
constexpr _string_view get_enum_full_string(TagType<en>) { \ | |
return #__VA_ARGS__; \ | |
} \ | |
} | |
#define MAKE_FANCY_ENUM(en, ...) \ | |
enum class en { __VA_ARGS__ }; \ | |
ENUM_TO_STR(en, __VA_ARGS__) | |
#define BITMASK_ENUM(en) \ | |
constexpr inline auto operator&(en lhs, en rhs) { \ | |
return to_enum<en>(from_enum(lhs) & from_enum(rhs)); \ | |
} \ | |
constexpr inline auto operator|(en lhs, en rhs) { \ | |
return to_enum<en>(from_enum(lhs) | from_enum(rhs)); \ | |
} \ | |
constexpr inline auto operator^(en lhs, en rhs) { \ | |
return to_enum<en>(from_enum(lhs) ^ from_enum(rhs)); \ | |
} \ | |
constexpr inline auto operator~(en val) { \ | |
return to_enum<en>(~from_enum(val)); \ | |
} \ | |
constexpr inline auto& operator&=(en& lhs, en rhs) { \ | |
lhs = lhs & rhs; \ | |
return lhs; \ | |
} \ | |
constexpr inline auto& operator|=(en& lhs, en rhs) { \ | |
lhs = lhs | rhs; \ | |
return lhs; \ | |
} \ | |
constexpr inline auto& operator^=(en& lhs, en rhs) { \ | |
lhs = lhs ^ rhs; \ | |
return lhs; \ | |
} | |
[[noreturn]] inline void fatalfail() { | |
abort(); | |
unreachable; | |
} | |
inline void assert_expr(bool expr, const char* msg = nullptr) { | |
if (!expr) { | |
if (msg) puts(msg); | |
#ifdef BACKTRACE_AVAILABLE | |
BackTrace::print_backtrace(); | |
#endif | |
fatalfail(); | |
} | |
} | |
template <typename T, typename Tp> | |
concept SameAsConstPtr = | |
std::same_as<std::add_pointer_t<Tp>, T> || std::same_as<std::add_pointer_t<std::add_const_t<Tp>>, T>; | |
template <typename T, typename Tp> | |
concept SameAsConstRef = | |
std::same_as<std::add_lvalue_reference_t<Tp>, T> || std::same_as<std::add_lvalue_reference_t<std::add_const_t<Tp>>, T>; | |
template <typename Iter, typename Tp> | |
concept Iterator = requires(Iter it) { | |
{ it.operator->() } -> SameAsConstPtr<Tp>; | |
{ *it } -> SameAsConstRef<Tp>; | |
{ ++it }; | |
{ it++ }; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment