rename chapters

This commit is contained in:
Amar Mahmutbegovic
2025-02-09 13:11:21 +01:00
parent 8634accda5
commit 526e6ec009
2928 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
#pragma once
#include <functional>
#if __cplusplus >= 202002L
#include <stdx/tuple.hpp>
#else
#include <tuple>
#endif
namespace stdx {
inline namespace v1 {
namespace detail {
#if __cplusplus >= 202002L
template <typename... Ts> using result_tuple_t = stdx::tuple<Ts...>;
#define CONSTEXPR_INVOKE constexpr
#else
template <typename... Ts> using result_tuple_t = std::tuple<Ts...>;
#define CONSTEXPR_INVOKE
#endif
} // namespace detail
template <typename O, typename... Is>
using transform_result = detail::result_tuple_t<O, Is...>;
template <typename InputIt, typename OutputIt, typename Operation,
typename... InputItN>
CONSTEXPR_INVOKE auto transform(InputIt first, InputIt last, OutputIt d_first,
Operation op, InputItN... first_n)
-> transform_result<OutputIt, InputIt, InputItN...> {
while (first != last) {
*d_first = std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...),
++d_first;
}
return {d_first, first, first_n...};
}
template <typename InputIt, typename Size, typename OutputIt,
typename Operation, typename... InputItN>
CONSTEXPR_INVOKE auto transform_n(InputIt first, Size n, OutputIt d_first,
Operation op, InputItN... first_n)
-> transform_result<OutputIt, InputIt, InputItN...> {
while (n-- > 0) {
*d_first = std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...),
++d_first;
}
return {d_first, first, first_n...};
}
template <typename InputIt, typename Operation, typename... InputItN>
CONSTEXPR_INVOKE auto for_each(InputIt first, InputIt last, Operation op,
InputItN... first_n) -> Operation {
while (first != last) {
std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
}
return op;
}
template <typename InputIt, typename Size, typename Operation,
typename... InputItN>
CONSTEXPR_INVOKE auto for_each_n(InputIt first, Size n, Operation op,
InputItN... first_n) -> Operation {
while (n-- > 0) {
std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
}
return op;
}
#undef CONSTEXPR_INVOKE
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,128 @@
#pragma once
#include <conc/atomic.hpp>
#include <atomic>
#include <type_traits>
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if __cplusplus >= 202002L
#define CPP20(...) __VA_ARGS__
#else
#define CPP20(...)
#endif
namespace stdx {
inline namespace v1 {
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
template <typename T> class atomic {
static_assert(std::is_trivially_copyable_v<T> and
std::is_copy_constructible_v<T> and
std::is_move_constructible_v<T> and
std::is_copy_assignable_v<T> and
std::is_move_assignable_v<T>,
"Atomic values must be trivially copyable, copy "
"constructible and copy assignable");
using elem_t = ::atomic::atomic_type_t<T>;
constexpr static auto alignment = ::atomic::alignment_of<T>;
static_assert(std::is_convertible_v<elem_t, T>,
"::atomic::atomic_type_t specialization result must be "
"convertible to T");
static_assert(std::is_convertible_v<T, elem_t>,
"::atomic::atomic_type_t specialization result must be "
"convertible from T");
alignas(alignment) elem_t value;
public:
using value_type = T;
constexpr atomic() CPP20(requires std::is_default_constructible_v<elem_t>)
: value{} {}
constexpr explicit atomic(T t) : value{static_cast<elem_t>(t)} {}
atomic(atomic const &) = delete;
auto operator=(atomic const &) -> atomic & = delete;
[[nodiscard]] auto
load(std::memory_order mo = std::memory_order_seq_cst) const -> T {
return static_cast<T>(::atomic::load(value, mo));
}
void store(T t, std::memory_order mo = std::memory_order_seq_cst) {
::atomic::store(value, static_cast<elem_t>(t), mo);
}
// NOLINTNEXTLINE(google-explicit-constructor)
[[nodiscard]] operator T() const { return load(); }
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
auto operator=(T t) -> T {
store(t);
return t;
}
[[nodiscard]] auto
exchange(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
return ::atomic::exchange(value, static_cast<elem_t>(t), mo);
}
auto fetch_add(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t + t; }, "T must support operator+(x, y)"));
return ::atomic::fetch_add(value, static_cast<elem_t>(t), mo);
}
auto fetch_sub(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t - t; }, "T must support operator-(x, y)"));
return ::atomic::fetch_sub(value, static_cast<elem_t>(t), mo);
}
auto operator+=(T t) -> T { return fetch_add(t) + t; }
auto operator-=(T t) -> T { return fetch_sub(t) - t; }
auto operator++() -> T {
CPP20(static_assert(
requires(T t) { ++t; }, "T must support operator++()"));
return ::atomic::fetch_add(value, 1) + 1;
}
[[nodiscard]] auto operator++(int) -> T {
CPP20(static_assert(
requires(T t) { t++; }, "T must support operator++(int)"));
return ::atomic::fetch_add(value, 1);
}
auto operator--() -> T {
CPP20(static_assert(
requires(T t) { --t; }, "T must support operator--()"));
return ::atomic::fetch_sub(value, 1) - 1;
}
[[nodiscard]] auto operator--(int) -> T {
CPP20(static_assert(
requires(T t) { t--; }, "T must support operator--(int)"));
return ::atomic::fetch_sub(value, 1);
}
auto fetch_and(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t & t; }, "T must support operator&(x, y)"));
return ::atomic::fetch_and(value, static_cast<elem_t>(t), mo);
}
auto fetch_or(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t | t; }, "T must support operator|(x, y)"));
return ::atomic::fetch_or(value, static_cast<elem_t>(t), mo);
}
auto fetch_xor(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t ^ t; }, "T must support operator^(x, y)"));
return ::atomic::fetch_xor(value, static_cast<elem_t>(t), mo);
}
auto operator&=(T t) -> T { return fetch_and(t) & t; }
auto operator|=(T t) -> T { return fetch_or(t) | t; }
auto operator^=(T t) -> T { return fetch_xor(t) ^ t; }
};
} // namespace v1
} // namespace stdx
// NOLINTEND(cppcoreguidelines-macro-usage)

View File

@@ -0,0 +1,244 @@
#pragma once
#include <conc/atomic.hpp>
#include <stdx/bit.hpp>
#include <stdx/bitset.hpp>
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/ct_string.hpp>
#include <stdx/detail/bitset_common.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/udls.hpp>
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <iterator>
#include <limits>
#include <string_view>
namespace stdx {
inline namespace v1 {
template <auto Size,
typename StorageElem = decltype(smallest_uint<to_underlying(Size)>())>
class atomic_bitset {
constexpr static std::size_t N = to_underlying(Size);
using elem_t = atomic::atomic_type_t<StorageElem>;
constexpr static auto alignment = atomic::alignment_of<StorageElem>;
static_assert(std::is_unsigned_v<elem_t>,
"Storage element for atomic_bitset must be an unsigned type");
constexpr static auto bit = elem_t{1U};
static_assert(N <= std::numeric_limits<elem_t>::digits,
"atomic_bitset is limited to a single storage element");
alignas(alignment) elem_t storage{};
constexpr static auto mask = bit_mask<elem_t, N - 1>();
auto salient_value(std::memory_order order) const -> elem_t {
return atomic::load(storage, order) & mask;
}
[[nodiscard]] static constexpr auto value_from_string(std::string_view str,
std::size_t pos,
std::size_t n,
char one) -> elem_t {
elem_t ret{};
auto const len = std::min(n, str.size() - pos);
auto const s = str.substr(pos, std::min(len, N));
auto i = bit;
// NOLINTNEXTLINE(modernize-loop-convert)
for (auto it = std::rbegin(s); it != std::rend(s); ++it) {
if (*it == one) {
ret |= i;
}
i = static_cast<elem_t>(i << 1u);
}
return ret;
}
using bitset_t = bitset<Size, elem_t>;
public:
constexpr atomic_bitset() = default;
constexpr explicit atomic_bitset(std::uint64_t value)
: storage{static_cast<elem_t>(value & mask)} {}
template <typename... Bs>
constexpr explicit atomic_bitset(place_bits_t, Bs... bs)
: storage{static_cast<elem_t>(
(elem_t{} | ... |
static_cast<elem_t>(bit << to_underlying(bs))))} {}
constexpr explicit atomic_bitset(all_bits_t) : storage{mask} {}
constexpr explicit atomic_bitset(std::string_view str, std::size_t pos = 0,
std::size_t n = std::string_view::npos,
char one = '1')
: storage{value_from_string(str, pos, n, one)} {}
#if __cplusplus >= 202002L
constexpr explicit atomic_bitset(ct_string<N + 1> s)
: atomic_bitset{static_cast<std::string_view>(s)} {}
#endif
template <typename T>
[[nodiscard]] auto
to(std::memory_order order = std::memory_order_seq_cst) const -> T {
using U = underlying_type_t<T>;
static_assert(
unsigned_integral<U>,
"Conversion must be to an unsigned integral type or enum!");
static_assert(N <= std::numeric_limits<U>::digits,
"atomic_bitset must fit within T");
return static_cast<T>(salient_value(order));
}
[[nodiscard]] auto
to_natural(std::memory_order order = std::memory_order_seq_cst) const
-> StorageElem {
return static_cast<StorageElem>(salient_value(order));
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator bitset_t() const {
return bitset_t{salient_value(std::memory_order_seq_cst)};
}
auto load(std::memory_order order = std::memory_order_seq_cst) const
-> bitset_t {
return bitset_t{salient_value(order)};
}
auto store(bitset_t b,
std::memory_order order = std::memory_order_seq_cst) {
atomic::store(storage, b.template to<elem_t>(), order);
}
constexpr static std::integral_constant<std::size_t, N> size{};
template <typename T> [[nodiscard]] auto operator[](T idx) const -> bool {
return load()[idx];
}
template <typename T>
auto set(T idx, bool value = true,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
if (value) {
return bitset_t{atomic::fetch_or(
storage, static_cast<elem_t>(bit << pos), order)};
}
return bitset_t{atomic::fetch_and(
storage, static_cast<elem_t>(~(bit << pos)), order)};
}
auto set(lsb_t lsb, msb_t msb, bool value = true,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const l = to_underlying(lsb);
auto const m = to_underlying(msb);
auto const shifted_value = bit_mask<elem_t>(m, l);
if (value) {
return bitset_t{atomic::fetch_or(storage, shifted_value, order)};
}
return bitset_t{atomic::fetch_and(storage, ~shifted_value, order)};
}
auto set(lsb_t lsb, length_t len, bool value = true,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const l = to_underlying(lsb);
auto const length = to_underlying(len);
return set(lsb, static_cast<msb_t>(l + length - 1), value, order);
}
auto set(std::memory_order order = std::memory_order_seq_cst)
LIFETIMEBOUND -> atomic_bitset & {
atomic::store(storage, mask, order);
return *this;
}
template <typename T>
auto reset(T idx, std::memory_order order = std::memory_order_seq_cst)
-> bitset_t {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
return bitset_t{atomic::fetch_and(
storage, static_cast<elem_t>(~(bit << pos)), order)};
}
auto
reset(lsb_t lsb, msb_t msb,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const l = to_underlying(lsb);
auto const m = to_underlying(msb);
auto const shifted_value = bit_mask<elem_t>(m, l);
return bitset_t{atomic::fetch_and(storage, ~shifted_value, order)};
}
auto
reset(lsb_t lsb, length_t len,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const l = to_underlying(lsb);
auto const length = to_underlying(len);
return reset(lsb, static_cast<msb_t>(l + length - 1), order);
}
auto reset(std::memory_order order = std::memory_order_seq_cst)
LIFETIMEBOUND -> atomic_bitset & {
atomic::store(storage, elem_t{}, order);
return *this;
}
template <typename T>
auto flip(T idx,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
return bitset_t{
atomic::fetch_xor(storage, static_cast<elem_t>(bit << pos), order)};
}
auto flip(lsb_t lsb, msb_t msb,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const l = to_underlying(lsb);
auto const m = to_underlying(msb);
auto const shifted_value = bit_mask<elem_t>(m, l);
return bitset_t{atomic::fetch_xor(storage, shifted_value, order)};
}
auto flip(lsb_t lsb, length_t len,
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
auto const l = to_underlying(lsb);
auto const length = to_underlying(len);
return flip(lsb, static_cast<msb_t>(l + length - 1), order);
}
auto flip(std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
return bitset_t{atomic::fetch_xor(storage, mask, order)};
}
[[nodiscard]] auto
all(std::memory_order order = std::memory_order_seq_cst) const -> bool {
return salient_value(order) == mask;
}
[[nodiscard]] auto
any(std::memory_order order = std::memory_order_seq_cst) const -> bool {
return salient_value(order) != 0;
}
[[nodiscard]] auto
none(std::memory_order order = std::memory_order_seq_cst) const -> bool {
return salient_value(order) == 0;
}
[[nodiscard]] auto
count(std::memory_order order = std::memory_order_seq_cst) const
-> std::size_t {
return static_cast<std::size_t>(popcount(salient_value(order)));
}
};
#if __cplusplus >= 202002L
template <std::size_t N> atomic_bitset(ct_string<N>) -> atomic_bitset<N - 1>;
#endif
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,396 @@
#pragma once
#include <stdx/concepts.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/utility.hpp>
#include <array>
#include <climits>
#include <cstdint>
#include <limits>
#include <type_traits>
#if __has_include(<bit>)
#include <bit>
#endif
// NOLINTBEGIN(modernize-use-constraints)
namespace stdx {
inline namespace v1 {
// endian
#if __cpp_lib_endian < 201907L
// NOLINTNEXTLINE(performance-enum-size)
enum struct endian : underlying_type_t<decltype(__BYTE_ORDER__)> {
little = __ORDER_LITTLE_ENDIAN__,
big = __ORDER_BIG_ENDIAN__,
native = __BYTE_ORDER__
};
#else
using endian = std::endian;
#endif
// bit_cast
#if __cpp_lib_bit_cast < 201806L
template <typename To, typename From>
[[nodiscard]] constexpr auto bit_cast(From &&from) noexcept -> To {
return __builtin_bit_cast(To, from);
}
#else
using std::bit_cast;
#endif
// byteswap
#if __cpp_lib_byteswap < 202110L
template <typename T>
[[nodiscard]] constexpr auto
byteswap(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if constexpr (sizeof(T) == sizeof(std::uint16_t)) {
return __builtin_bswap16(x);
} else if constexpr (sizeof(T) == sizeof(std::uint32_t)) {
return __builtin_bswap32(x);
} else if constexpr (sizeof(T) == sizeof(std::uint64_t)) {
return __builtin_bswap64(x);
} else {
return x;
}
}
#else
using std::byteswap;
#endif
// bit ops
#if __cpp_lib_bitops < 201907L
template <typename T>
[[nodiscard]] constexpr auto
countl_zero(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, int> {
if (x == 0) {
return std::numeric_limits<T>::digits;
}
if constexpr (sizeof(T) == sizeof(unsigned int)) {
return __builtin_clz(x);
} else if constexpr (sizeof(T) ==
sizeof(unsigned long)) { // NOLINT(google-runtime-int)
return __builtin_clzl(x);
} else if constexpr (
sizeof(T) == sizeof(unsigned long long)) { // NOLINT(google-runtime-int)
return __builtin_clzll(x);
} else {
return __builtin_clzll(x) + std::numeric_limits<T>::digits -
std::numeric_limits<
unsigned long long>::digits; // NOLINT(google-runtime-int)
}
}
template <typename T>
[[nodiscard]] constexpr auto
countr_zero(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, int> {
if (x == 0) {
return std::numeric_limits<T>::digits;
}
if constexpr (sizeof(T) == sizeof(unsigned int)) {
return __builtin_ctz(x);
} else if constexpr (sizeof(T) ==
sizeof(unsigned long)) { // NOLINT(google-runtime-int)
return __builtin_ctzl(x);
} else {
return __builtin_ctzll(x);
}
}
template <typename T>
[[nodiscard]] constexpr auto
countl_one(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, int> {
return countl_zero(T(~x));
}
template <typename T>
[[nodiscard]] constexpr auto
countr_one(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, int> {
return countr_zero(T(~x));
}
template <typename T>
[[nodiscard]] constexpr auto
popcount(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, int> {
if constexpr (sizeof(T) <= sizeof(unsigned int)) {
return __builtin_popcount(x);
} else if constexpr (sizeof(T) <=
sizeof(unsigned long)) { // NOLINT(google-runtime-int)
return __builtin_popcountl(x);
} else {
return __builtin_popcountll(x);
}
}
namespace detail {
template <typename T>
[[nodiscard]] constexpr auto
rotl(T x, T s) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
#ifdef __clang__
if constexpr (sizeof(T) == sizeof(std::uint8_t)) {
return __builtin_rotateleft8(x, s);
} else if constexpr (sizeof(T) == sizeof(std::uint16_t)) {
return __builtin_rotateleft16(x, s);
} else if constexpr (sizeof(T) == sizeof(std::uint32_t)) {
return __builtin_rotateleft32(x, s);
} else {
return __builtin_rotateleft64(x, s);
}
#else
return static_cast<T>((x << s) |
(x >> (std::numeric_limits<T>::digits - s)));
#endif
}
template <typename T>
[[nodiscard]] constexpr auto
rotr(T x, T s) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
#ifdef __clang__
if constexpr (sizeof(T) == sizeof(std::uint8_t)) {
return __builtin_rotateright8(x, s);
} else if constexpr (sizeof(T) == sizeof(std::uint16_t)) {
return __builtin_rotateright16(x, s);
} else if constexpr (sizeof(T) == sizeof(std::uint32_t)) {
return __builtin_rotateright32(x, s);
} else {
return __builtin_rotateright64(x, s);
}
#else
return static_cast<T>((x >> s) |
(x << (std::numeric_limits<T>::digits - s)));
#endif
}
} // namespace detail
template <typename T>
[[nodiscard]] constexpr auto
rotl(T x, int s) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if (s == 0) {
return x;
}
if (s < 0) {
return detail::rotr(x, T(-s));
}
return detail::rotl(x, T(s));
}
template <typename T>
[[nodiscard]] constexpr auto
rotr(T x, int s) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if (s == 0) {
return x;
}
if (s < 0) {
return detail::rotl(x, T(-s));
}
return detail::rotr(x, T(s));
}
#else
using std::countl_one;
using std::countl_zero;
using std::countr_one;
using std::countr_zero;
using std::popcount;
using std::rotl;
using std::rotr;
#endif
// pow2
#if __cpp_lib_int_pow2 < 202002L
template <typename T>
[[nodiscard]] constexpr auto
has_single_bit(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, bool> {
return x and not(x & (x - 1));
}
template <typename T>
[[nodiscard]] constexpr auto
bit_width(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, int> {
return std::numeric_limits<T>::digits - countl_zero(x);
}
template <typename T>
[[nodiscard]] constexpr auto
bit_ceil(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if (x <= 1U) {
return 1U;
}
return T(1U << bit_width(x));
}
template <typename T>
[[nodiscard]] constexpr auto
bit_floor(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if (x == 0) {
return x;
}
return T(1U << (bit_width(x) - 1));
}
#else
using std::bit_ceil;
using std::bit_floor;
using std::bit_width;
using std::has_single_bit;
#endif
template <typename T>
[[nodiscard]] constexpr auto
to_le(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if constexpr (stdx::endian::native == stdx::endian::big) {
return byteswap(x);
} else {
return x;
}
}
template <typename T>
[[nodiscard]] constexpr auto
to_be(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if constexpr (stdx::endian::native == stdx::endian::little) {
return byteswap(x);
} else {
return x;
}
}
template <typename T>
[[nodiscard]] constexpr auto
from_le(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if constexpr (stdx::endian::native == stdx::endian::big) {
return byteswap(x);
} else {
return x;
}
}
template <typename T>
[[nodiscard]] constexpr auto
from_be(T x) noexcept -> std::enable_if_t<std::is_unsigned_v<T>, T> {
if constexpr (stdx::endian::native == stdx::endian::little) {
return byteswap(x);
} else {
return x;
}
}
template <typename To>
constexpr auto bit_pack = [](auto... args) {
static_assert(stdx::always_false_v<To, decltype(args)...>,
"bit_pack is undefined for those types");
};
template <>
constexpr inline auto bit_pack<std::uint16_t> =
[](std::uint8_t hi, std::uint8_t lo) -> std::uint16_t {
return static_cast<std::uint16_t>((static_cast<std::uint32_t>(hi) << 8u) |
lo);
};
template <>
constexpr inline auto bit_pack<std::uint32_t> =
stdx::overload{[](std::uint16_t hi, std::uint16_t lo) -> std::uint32_t {
return (static_cast<std::uint32_t>(hi) << 16u) | lo;
},
[](std::uint8_t b0, std::uint8_t b1, std::uint8_t b2,
std::uint8_t b3) -> std::uint32_t {
return (static_cast<std::uint32_t>(b0) << 24u) |
(static_cast<std::uint32_t>(b1) << 16u) |
(static_cast<std::uint32_t>(b2) << 8u) | b3;
}};
template <>
constexpr inline auto bit_pack<std::uint64_t> =
stdx::overload{[](std::uint32_t hi, std::uint32_t lo) -> std::uint64_t {
return (static_cast<std::uint64_t>(hi) << 32u) | lo;
},
[](std::uint16_t w0, std::uint16_t w1, std::uint16_t w2,
std::uint16_t w3) -> std::uint64_t {
return (static_cast<std::uint64_t>(w0) << 48u) |
(static_cast<std::uint64_t>(w1) << 32u) |
(static_cast<std::uint64_t>(w2) << 16u) | w3;
},
[](std::uint8_t b0, std::uint8_t b1, std::uint8_t b2,
std::uint8_t b3, std::uint8_t b4, std::uint8_t b5,
std::uint8_t b6, std::uint8_t b7) -> std::uint64_t {
return (static_cast<std::uint64_t>(b0) << 56u) |
(static_cast<std::uint64_t>(b1) << 48u) |
(static_cast<std::uint64_t>(b2) << 40u) |
(static_cast<std::uint64_t>(b3) << 32u) |
(static_cast<std::uint64_t>(b4) << 24u) |
(static_cast<std::uint64_t>(b5) << 16u) |
(static_cast<std::uint64_t>(b6) << 8u) | b7;
}};
template <typename To, typename From> constexpr auto bit_unpack(From arg) {
static_assert(unsigned_integral<To> and unsigned_integral<From>,
"bit_unpack is undefined for those types");
constexpr auto sz = sized<From>{1}.template in<To>();
auto r = bit_cast<std::array<To, sz>>(to_be(arg));
for (auto &elem : r) {
elem = from_be(elem);
}
return r;
}
namespace detail {
template <typename T, std::size_t Bit>
constexpr auto
mask_bits() -> std::enable_if_t<Bit <= std::numeric_limits<T>::digits, T> {
if constexpr (Bit == std::numeric_limits<T>::digits) {
return std::numeric_limits<T>::max();
} else {
return static_cast<T>(T{1} << Bit) - T{1};
}
}
template <typename T> constexpr auto mask_bits(std::size_t Bit) -> T {
if (Bit == std::numeric_limits<T>::digits) {
return std::numeric_limits<T>::max();
}
return static_cast<T>(T{1} << Bit) - T{1};
}
} // namespace detail
template <typename T, std::size_t Msb = std::numeric_limits<T>::digits - 1,
std::size_t Lsb = 0>
[[nodiscard]] constexpr auto bit_mask() noexcept
-> std::enable_if_t<std::is_unsigned_v<T> and Msb >= Lsb, T> {
static_assert(Msb < std::numeric_limits<T>::digits);
return detail::mask_bits<T, Msb + 1>() - detail::mask_bits<T, Lsb>();
}
template <typename T>
[[nodiscard]] constexpr auto bit_mask(std::size_t Msb,
std::size_t Lsb = 0) noexcept
-> std::enable_if_t<std::is_unsigned_v<T>, T> {
return detail::mask_bits<T>(Msb + 1) - detail::mask_bits<T>(Lsb);
}
template <typename T> constexpr auto bit_size() -> std::size_t {
return sizeof(T) * CHAR_BIT;
}
template <std::size_t N> CONSTEVAL auto smallest_uint() {
if constexpr (N <= std::numeric_limits<std::uint8_t>::digits) {
return std::uint8_t{};
} else if constexpr (N <= std::numeric_limits<std::uint16_t>::digits) {
return std::uint16_t{};
} else if constexpr (N <= std::numeric_limits<std::uint32_t>::digits) {
return std::uint32_t{};
} else {
return std::uint64_t{};
}
}
template <std::size_t N> using smallest_uint_t = decltype(smallest_uint<N>());
} // namespace v1
} // namespace stdx
// NOLINTEND(modernize-use-constraints)

View File

@@ -0,0 +1,428 @@
#pragma once
#include <stdx/bit.hpp>
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/ct_string.hpp>
#include <stdx/detail/bitset_common.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/udls.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <string_view>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <auto Size,
typename StorageElem = decltype(smallest_uint<to_underlying(Size)>())>
class bitset {
constexpr static std::size_t N = to_underlying(Size);
using elem_t = StorageElem;
static_assert(std::is_unsigned_v<elem_t>,
"Storage element for bitset must be an unsigned type");
constexpr static auto storage_elem_size =
std::numeric_limits<elem_t>::digits;
constexpr static auto storage_size =
(N + storage_elem_size - 1) / storage_elem_size;
constexpr static auto bit = elem_t{1U};
constexpr static auto allbits = std::numeric_limits<elem_t>::max();
std::array<elem_t, storage_size> storage{};
constexpr static auto lastmask = []() -> elem_t {
if constexpr (N % storage_elem_size != 0) {
return allbits >> (storage_elem_size - N % storage_elem_size);
} else {
return allbits;
}
}();
constexpr auto highbits() const -> elem_t {
return storage.back() & lastmask;
}
[[nodiscard]] constexpr static auto indices(std::size_t pos) {
struct locator {
std::size_t index;
std::size_t offset;
};
return locator{pos / storage_elem_size, pos % storage_elem_size};
}
[[nodiscard]] friend constexpr auto operator==(bitset const &lhs,
bitset const &rhs) -> bool {
for (auto i = std::size_t{}; i < storage_size - 1; ++i) {
if (lhs.storage[i] != rhs.storage[i]) {
return false;
}
}
return lhs.highbits() == rhs.highbits();
}
#if __cpp_impl_three_way_comparison < 201907L
[[nodiscard]] friend constexpr auto operator!=(bitset const &lhs,
bitset const &rhs) -> bool {
return not(lhs == rhs);
}
#endif
friend constexpr auto operator|(bitset lhs, bitset const &rhs) -> bitset {
lhs |= rhs;
return lhs;
}
friend constexpr auto operator&(bitset lhs, bitset const &rhs) -> bitset {
lhs &= rhs;
return lhs;
}
friend constexpr auto operator^(bitset lhs, bitset const &rhs) -> bitset {
lhs ^= rhs;
return lhs;
}
friend constexpr auto operator-(bitset const &lhs, bitset rhs) -> bitset {
rhs.flip();
return lhs & rhs;
}
friend constexpr auto operator<<(bitset lhs, std::size_t pos) -> bitset {
lhs <<= pos;
return lhs;
}
friend constexpr auto operator>>(bitset lhs, std::size_t pos) -> bitset {
lhs >>= pos;
return lhs;
}
template <typename F> constexpr auto for_each(F &&f) const -> F {
std::size_t i = 0;
for (auto e : storage) {
while (e != 0) {
auto const offset = static_cast<std::size_t>(countr_zero(e));
e &= static_cast<elem_t>(~(bit << offset));
f(i + offset);
}
i += std::numeric_limits<elem_t>::digits;
}
return std::forward<F>(f);
}
template <typename F, auto M, typename... S>
friend constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F;
public:
constexpr bitset() = default;
constexpr explicit bitset(std::uint64_t value) {
if constexpr (std::is_same_v<elem_t, std::uint64_t>) {
storage[0] = value;
} else {
for (auto &elem : storage) {
if (value == 0) {
break;
}
elem = value & allbits;
value >>= storage_elem_size;
}
}
}
template <typename... Bs>
constexpr explicit bitset(place_bits_t, Bs... bs) {
static_assert(((std::is_integral_v<Bs> or std::is_enum_v<Bs>) and ...),
"Bit places must be integral or enumeration types!");
(set(static_cast<std::size_t>(bs)), ...);
}
constexpr explicit bitset(all_bits_t) {
for (auto &elem : storage) {
elem = allbits;
}
storage.back() &= lastmask;
}
constexpr explicit bitset(std::string_view str, std::size_t pos = 0,
std::size_t n = std::string_view::npos,
char one = '1') {
auto const len = std::min(n, str.size() - pos);
auto i = std::size_t{};
auto const s = str.substr(pos, std::min(len, N));
// NOLINTNEXTLINE(modernize-loop-convert)
for (auto it = std::rbegin(s); it != std::rend(s); ++it) {
set(i++, *it == one);
}
}
#if __cplusplus >= 202002L
constexpr explicit bitset(ct_string<N + 1> s)
: bitset{static_cast<std::string_view>(s)} {}
#endif
template <typename T> [[nodiscard]] constexpr auto to() const -> T {
using U = underlying_type_t<T>;
static_assert(
unsigned_integral<U>,
"Conversion must be to an unsigned integral type or enum!");
static_assert(N <= std::numeric_limits<U>::digits,
"Bitset too big for conversion to T");
if constexpr (std::is_same_v<elem_t, U>) {
return static_cast<T>(storage[0] & lastmask);
} else {
U result{highbits()};
for (auto i = storage_size - 2u; i < storage_size; --i) {
result = static_cast<T>(result << storage_elem_size);
result |= storage[i];
}
return static_cast<T>(result);
}
}
[[nodiscard]] constexpr auto to_natural() const {
using T = smallest_uint_t<N>;
static_assert(N <= std::numeric_limits<T>::digits,
"Bitset too big for conversion to T");
return to<T>();
}
constexpr static std::integral_constant<std::size_t, N> size{};
template <typename T>
[[nodiscard]] constexpr auto operator[](T idx) const -> bool {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
return (storage[index] & (bit << offset)) != 0;
}
template <typename T>
constexpr auto set(T idx, bool value = true) LIFETIMEBOUND -> bitset & {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
if (value) {
storage[index] |= static_cast<elem_t>(bit << offset);
} else {
storage[index] &= static_cast<elem_t>(~(bit << offset));
}
return *this;
}
constexpr auto set(lsb_t lsb, msb_t msb,
bool value = true) LIFETIMEBOUND -> bitset & {
auto const l = to_underlying(lsb);
auto const m = to_underlying(msb);
auto [l_index, l_offset] = indices(l);
auto const [m_index, m_offset] = indices(m);
using setfn = auto (*)(elem_t *, elem_t)->void;
auto const fn = [&]() -> setfn {
if (value) {
return [](elem_t *ptr, elem_t val) { *ptr |= val; };
}
return [](elem_t *ptr, elem_t val) { *ptr &= ~val; };
}();
auto l_mask = std::numeric_limits<elem_t>::max() << l_offset;
if (l_index != m_index) {
fn(&storage[l_index++], static_cast<elem_t>(l_mask));
l_mask = std::numeric_limits<elem_t>::max();
}
while (l_index != m_index) {
fn(&storage[l_index++], static_cast<elem_t>(l_mask));
}
auto const m_mask = std::numeric_limits<elem_t>::max() >>
(storage_elem_size - m_offset - 1);
fn(&storage[l_index], static_cast<elem_t>(l_mask & m_mask));
return *this;
}
constexpr auto set(lsb_t lsb, length_t len,
bool value = true) LIFETIMEBOUND -> bitset & {
auto const l = to_underlying(lsb);
auto const length = to_underlying(len);
return set(lsb, static_cast<msb_t>(l + length - 1), value);
}
constexpr auto set() LIFETIMEBOUND -> bitset & {
for (auto &elem : storage) {
elem = allbits;
}
return *this;
}
template <typename T>
constexpr auto reset(T idx) LIFETIMEBOUND -> bitset & {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
storage[index] &= static_cast<elem_t>(~(bit << offset));
return *this;
}
constexpr auto reset() LIFETIMEBOUND -> bitset & {
for (auto &elem : storage) {
elem = {};
}
return *this;
}
constexpr auto reset(lsb_t lsb, msb_t msb) LIFETIMEBOUND -> bitset & {
return set(lsb, msb, false);
}
constexpr auto reset(lsb_t lsb, length_t len) LIFETIMEBOUND -> bitset & {
return set(lsb, len, false);
}
template <typename T> constexpr auto flip(T idx) LIFETIMEBOUND -> bitset & {
auto const pos = static_cast<std::size_t>(to_underlying(idx));
auto const [index, offset] = indices(pos);
storage[index] ^= static_cast<elem_t>(bit << offset);
return *this;
}
constexpr auto flip() LIFETIMEBOUND -> bitset & {
for (auto &elem : storage) {
elem ^= allbits;
}
return *this;
}
[[nodiscard]] constexpr auto all() const -> bool {
for (auto i = std::size_t{}; i < storage_size - 1; ++i) {
if (storage[i] != allbits) {
return false;
}
}
return highbits() == lastmask;
}
[[nodiscard]] constexpr auto any() const -> bool {
for (auto i = std::size_t{}; i < storage_size - 1; ++i) {
if (storage[i] != 0) {
return true;
}
}
return highbits() != 0;
}
[[nodiscard]] constexpr auto none() const -> bool { return not any(); }
[[nodiscard]] constexpr auto count() const -> std::size_t {
std::size_t n{};
for (auto i = std::size_t{}; i < storage_size - 1; ++i) {
n += static_cast<std::size_t>(popcount(storage[i]));
}
return n + static_cast<std::size_t>(popcount(highbits()));
}
[[nodiscard]] constexpr auto lowest_unset() const -> std::size_t {
std::size_t i = 0;
for (auto e : storage) {
if (auto offset = static_cast<std::size_t>(countr_one(e));
offset != std::numeric_limits<elem_t>::digits) {
return i + offset;
}
i += std::numeric_limits<elem_t>::digits;
}
return i;
}
[[nodiscard]] constexpr auto operator~() const -> bitset {
bitset result{};
for (auto i = std::size_t{}; i < storage_size; ++i) {
result.storage[i] = ~storage[i];
}
return result;
}
constexpr auto operator|=(bitset const &rhs) LIFETIMEBOUND->bitset & {
for (auto i = std::size_t{}; i < storage_size; ++i) {
storage[i] |= rhs.storage[i];
}
return *this;
}
constexpr auto operator&=(bitset const &rhs) LIFETIMEBOUND->bitset & {
for (auto i = std::size_t{}; i < storage_size; ++i) {
storage[i] &= rhs.storage[i];
}
return *this;
}
constexpr auto operator^=(bitset const &rhs) LIFETIMEBOUND->bitset & {
for (auto i = std::size_t{}; i < storage_size; ++i) {
storage[i] ^= rhs.storage[i];
}
return *this;
}
constexpr auto operator<<=(std::size_t pos) LIFETIMEBOUND->bitset & {
auto dst = storage_size - 1;
auto const start = dst - pos / storage_elem_size;
pos %= storage_elem_size;
if (pos == 0) {
for (auto i = start; i > std::size_t{}; --i) {
storage[dst] = storage[i];
--dst;
}
} else {
auto const borrow_shift = storage_elem_size - pos;
for (auto i = start; i > std::size_t{}; --i) {
storage[dst] = static_cast<elem_t>(storage[i] << pos);
storage[dst] |=
static_cast<elem_t>(storage[i - 1] >> borrow_shift);
--dst;
}
}
storage[dst] = static_cast<elem_t>(storage.front() << pos);
while (dst > std::size_t{}) {
storage[--dst] = 0;
}
return *this;
}
constexpr auto operator>>=(std::size_t pos) LIFETIMEBOUND->bitset & {
auto dst = std::size_t{};
auto const start = pos / storage_elem_size;
pos %= storage_elem_size;
if (pos == 0) {
for (auto i = start; i < storage_size - 1; ++i) {
storage[dst] = storage[i];
++dst;
}
} else {
auto const borrow_shift = storage_elem_size - pos;
for (auto i = start; i < storage_size - 1; ++i) {
storage[dst] = static_cast<elem_t>(storage[i] >> pos);
storage[dst] |=
static_cast<elem_t>(storage[i + 1] << borrow_shift);
++dst;
}
}
storage[dst++] = static_cast<elem_t>(storage.back() >> pos);
while (dst < storage_size) {
storage[dst++] = 0;
}
return *this;
}
};
template <typename F, auto M, typename... S>
constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F {
if constexpr (sizeof...(bs) == 1) {
return (bs.for_each(std::forward<F>(f)), ...);
} else {
static_assert(stdx::always_false_v<F>, "unimplemented");
return f;
}
}
#if __cplusplus >= 202002L
template <std::size_t N> bitset(ct_string<N>) -> bitset<N - 1>;
#endif
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,228 @@
#pragma once
#include <stdx/bit.hpp>
#include <stdx/memory.hpp>
#include <stdx/utility.hpp>
#include <cstddef>
#include <cstring>
#include <functional>
#include <iterator>
#include <type_traits>
// NOLINTBEGIN(modernize-use-constraints)
namespace stdx {
inline namespace v1 {
namespace detail {
template <typename It>
constexpr auto is_byteratorish_v =
std::is_base_of_v<std::random_access_iterator_tag,
typename std::iterator_traits<It>::iterator_category> and
std::is_trivially_copyable_v<typename std::iterator_traits<It>::value_type>;
template <typename It>
constexpr auto iterator_value_type()
-> decltype(*std::declval<typename std::iterator_traits<It>::pointer>());
template <typename It>
using iterator_value_t = decltype(iterator_value_type<It>());
} // namespace detail
template <typename T> class byterator {
using byte_t = std::remove_reference_t<forward_like_t<T, std::byte>>;
byte_t *ptr;
[[nodiscard]] constexpr friend auto operator==(byterator const &x,
byterator const &y) -> bool {
return x.ptr == y.ptr;
}
template <typename It,
std::enable_if_t<std::is_same_v<detail::iterator_value_t<It>, T>,
int> = 0>
[[nodiscard]] constexpr friend auto operator==(byterator const &x,
It y) -> bool {
return static_cast<void const *>(x.ptr) ==
static_cast<void const *>(stdx::to_address(y));
}
#if __cpp_impl_three_way_comparison >= 201907L
[[nodiscard]] constexpr friend auto operator<=>(byterator const &x,
byterator const &y) {
return x.ptr <=> y.ptr;
}
#else
[[nodiscard]] constexpr friend auto operator!=(byterator const &x,
byterator const &y) -> bool {
return not(x == y);
}
template <typename It>
[[nodiscard]] constexpr friend auto operator==(It y,
byterator const &x) -> bool {
return x == y;
}
template <typename It>
[[nodiscard]] constexpr friend auto operator!=(byterator const &x,
It y) -> bool {
return not(x == y);
}
template <typename It>
[[nodiscard]] constexpr friend auto operator!=(It y,
byterator const &x) -> bool {
return not(x == y);
}
[[nodiscard]] constexpr friend auto operator<(byterator const &x,
byterator const &y) -> bool {
return std::less{}(x.ptr, y.ptr);
}
[[nodiscard]] constexpr friend auto operator<=(byterator const &x,
byterator const &y) -> bool {
return std::less_equal{}(x.ptr, y.ptr);
}
[[nodiscard]] constexpr friend auto operator>(byterator const &x,
byterator const &y) -> bool {
return std::greater{}(x.ptr, y.ptr);
}
[[nodiscard]] constexpr friend auto operator>=(byterator const &x,
byterator const &y) -> bool {
return std::greater_equal{}(x.ptr, y.ptr);
}
#endif
public:
using difference_type = std::ptrdiff_t;
using value_type = std::byte;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::random_access_iterator_tag;
template <typename It,
std::enable_if_t<detail::is_byteratorish_v<It>, int> = 0>
explicit byterator(It it) : ptr(bit_cast<byte_t *>(stdx::to_address(it))) {}
[[nodiscard]] constexpr auto operator->() const -> byte_t * { return ptr; }
[[nodiscard]] constexpr auto operator*() const -> byte_t & { return *ptr; }
constexpr auto operator++() -> byterator & {
++ptr;
return *this;
}
[[nodiscard]] constexpr auto operator++(int) -> byterator {
auto tmp = *this;
++(*this);
return tmp;
}
constexpr auto operator--() -> byterator & {
--ptr;
return *this;
}
[[nodiscard]] constexpr auto operator--(int) -> byterator {
auto tmp = *this;
--(*this);
return tmp;
}
constexpr auto operator+=(difference_type d) -> byterator & {
ptr += d;
return *this;
}
constexpr auto operator-=(difference_type d) -> byterator & {
ptr -= d;
return *this;
}
[[nodiscard]] friend constexpr auto
operator+(byterator i, difference_type d) -> byterator {
i += d;
return i;
}
[[nodiscard]] friend constexpr auto operator+(difference_type d,
byterator i) -> byterator {
i += d;
return i;
}
[[nodiscard]] friend constexpr auto
operator-(byterator i, difference_type d) -> byterator {
i -= d;
return i;
}
[[nodiscard]] friend constexpr auto
operator-(byterator x, byterator y) -> difference_type {
return x.ptr - y.ptr;
}
[[nodiscard]] constexpr auto operator[](difference_type n) -> byte_t & {
return ptr[n];
}
[[nodiscard]] constexpr auto
operator[](difference_type n) const -> byte_t const & {
return ptr[n];
}
template <typename V = std::uint8_t, typename R = V,
std::enable_if_t<std::is_trivially_copyable_v<V>, int> = 0>
[[nodiscard]] auto peek() -> R {
V v;
std::memcpy(std::addressof(v), ptr, sizeof(V));
return static_cast<R>(v);
}
template <typename V = std::uint8_t, typename R = V,
std::enable_if_t<std::is_trivially_copyable_v<V>, int> = 0>
[[nodiscard]] auto read() -> R {
R ret = peek<V, R>();
ptr += sizeof(V);
return ret;
}
template <typename V,
std::enable_if_t<std::is_trivially_copyable_v<remove_cvref_t<V>>,
int> = 0>
auto write(V &&v) -> void {
using R = remove_cvref_t<V>;
std::memcpy(ptr, std::addressof(v), sizeof(R));
ptr += sizeof(R);
}
template <typename V = std::uint8_t> [[nodiscard]] auto peeku8() {
return peek<std::uint8_t, V>();
}
template <typename V = std::uint8_t> [[nodiscard]] auto readu8() {
return read<std::uint8_t, V>();
}
template <typename V> [[nodiscard]] auto writeu8(V &&v) {
return write(static_cast<std::uint8_t>(std::forward<V>(v)));
}
template <typename V = std::uint16_t> [[nodiscard]] auto peeku16() {
return peek<std::uint16_t, V>();
}
template <typename V = std::uint16_t> [[nodiscard]] auto readu16() {
return read<std::uint16_t, V>();
}
template <typename V> [[nodiscard]] auto writeu16(V &&v) {
return write(static_cast<std::uint16_t>(std::forward<V>(v)));
}
template <typename V = std::uint32_t> [[nodiscard]] auto peeku32() {
return peek<std::uint32_t, V>();
}
template <typename V = std::uint32_t> [[nodiscard]] auto readu32() {
return read<std::uint32_t, V>();
}
template <typename V> [[nodiscard]] auto writeu32(V &&v) {
return write(static_cast<std::uint32_t>(std::forward<V>(v)));
}
};
template <typename It, std::enable_if_t<detail::is_byteratorish_v<It>, int> = 0>
byterator(It) -> byterator<detail::iterator_value_t<It>>;
} // namespace v1
} // namespace stdx
// NOLINTEND(modernize-use-constraints)

View File

@@ -0,0 +1,86 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/functional.hpp>
#include <stdx/type_traits.hpp>
#include <optional>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <typename F> struct cached {
using value_type = stdx::remove_cvref_t<std::invoke_result_t<F>>;
constexpr explicit cached(F const &f) : lazy{f} {}
constexpr explicit cached(F &&f) : lazy{std::move(f)} {}
constexpr auto has_value() const noexcept -> bool {
return opt.has_value();
}
constexpr explicit operator bool() const noexcept {
return opt.has_value();
}
constexpr auto value() & LIFETIMEBOUND -> value_type & {
populate();
return *opt;
}
constexpr auto value() const & LIFETIMEBOUND -> value_type const & {
populate();
return *opt;
}
constexpr auto value() && LIFETIMEBOUND -> value_type && {
populate();
return *std::move(opt);
}
constexpr auto value() const && LIFETIMEBOUND -> value_type const && {
populate();
return *std::move(opt);
}
constexpr auto operator->() const LIFETIMEBOUND->value_type const * {
populate();
return opt.operator->();
}
constexpr auto operator->() LIFETIMEBOUND->value_type * {
populate();
return opt.operator->();
}
constexpr auto operator*() const & LIFETIMEBOUND->decltype(auto) {
return value();
}
constexpr auto operator*() & LIFETIMEBOUND->decltype(auto) {
return value();
}
constexpr auto operator*() const && LIFETIMEBOUND->decltype(auto) {
return std::move(*this).value();
}
constexpr auto operator*() && LIFETIMEBOUND->decltype(auto) {
return std::move(*this).value();
}
auto reset() { opt.reset(); }
auto refresh() LIFETIMEBOUND -> value_type & {
opt.reset();
populate();
return *opt;
}
private:
constexpr auto populate() const {
if (not opt.has_value()) {
opt.emplace(lazy);
}
}
with_result_of<F> lazy;
mutable std::optional<value_type> opt{};
};
template <typename C>
using cached_value_t = typename stdx::remove_cvref_t<C>::value_type;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,68 @@
#pragma once
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#ifndef CONSTINIT
#ifndef __cpp_constinit
#if defined(__clang__)
#define CONSTINIT [[clang::require_constant_initialization]]
#else
#define CONSTINIT
#endif
#else
#define CONSTINIT constinit
#endif
#endif
#ifndef CONSTEVAL
#ifndef __cpp_consteval
#define CONSTEVAL constexpr
#else
#define CONSTEVAL consteval
#endif
#endif
#ifndef USING_ATTR_NS
#if defined(__clang__)
#define USING_ATTR_NS using clang:
#else
#define USING_ATTR_NS using gnu:
#endif
#endif
#ifndef ALWAYS_INLINE
#define ALWAYS_INLINE inline __attribute__((always_inline))
#endif
#ifndef NEVER_INLINE
#define NEVER_INLINE __attribute__((noinline))
#endif
#ifndef FLATTEN
#define FLATTEN __attribute__((flatten))
#endif
#ifndef MUSTTAIL
#if defined(__clang__)
#define MUSTTAIL [[clang::musttail]]
#else
#define MUSTTAIL
#endif
#endif
#ifndef LIFETIMEBOUND
#if defined(__clang__)
#define LIFETIMEBOUND [[clang::lifetimebound]]
#else
#define LIFETIMEBOUND
#endif
#endif
#define STDX_DO_PRAGMA(X) _Pragma(#X)
#ifdef __clang__
#define STDX_PRAGMA(X) STDX_DO_PRAGMA(clang X)
#else
#define STDX_PRAGMA(X) STDX_DO_PRAGMA(GCC X)
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)

View File

@@ -0,0 +1,228 @@
#pragma once
#include <stdx/type_traits.hpp>
#if __has_include(<concepts>)
#include <concepts>
#endif
#if __cpp_lib_concepts < 202002L
#include <functional>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
#if __cplusplus < 202002L
// Before C++20 we can't use concepts directly, but we can still allow the
// names to be used in constexpr contexts
template <typename T> constexpr auto integral = std::is_integral_v<T>;
template <typename T>
constexpr auto floating_point = std::is_floating_point_v<T>;
template <typename T>
constexpr auto signed_integral = integral<T> and std::is_signed_v<T>;
template <typename T>
constexpr auto unsigned_integral = integral<T> and std::is_unsigned_v<T>;
template <typename From, typename To>
constexpr auto convertible_to = std::is_convertible_v<From, To>;
template <typename T, typename U>
constexpr auto derived_from =
std::is_base_of_v<U, T> and
std::is_convertible_v<T const volatile *, U const volatile *>;
template <typename T, typename U>
constexpr auto same_as = std::is_same_v<T, U> and std::is_same_v<U, T>;
template <typename T, typename U>
constexpr auto same_as_unqualified =
is_same_unqualified_v<T, U> and is_same_unqualified_v<U, T>;
// NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
#define DETECTOR(name, expr) \
namespace detail::detect { \
template <typename T, typename = void> constexpr auto name = false; \
template <typename T> \
constexpr auto name<T, std::void_t<decltype(expr)>> = true; \
}
// NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
DETECTOR(eq_compare, (std::declval<T>() == std::declval<T>()))
DETECTOR(neq_compare, (std::declval<T>() != std::declval<T>()))
template <typename T>
constexpr auto equality_comparable =
detail::detect::eq_compare<T> and detail::detect::neq_compare<T>;
DETECTOR(lt_compare, (std::declval<T>() < std::declval<T>()))
DETECTOR(lte_compare, (std::declval<T>() <= std::declval<T>()))
DETECTOR(gt_compare, (std::declval<T>() > std::declval<T>()))
DETECTOR(gte_compare, (std::declval<T>() >= std::declval<T>()))
template <typename T>
constexpr auto totally_ordered =
equality_comparable<T> and detail::detect::lt_compare<T> and
detail::detect::lte_compare<T> and detail::detect::gt_compare<T> and
detail::detect::gte_compare<T>;
namespace detail::detect {
template <typename... Args> struct arg_list {
template <typename F>
using invoke_result_t = std::invoke_result_t<F, Args...>;
};
template <typename F, typename Args, typename = void>
constexpr auto invocable = false;
template <typename F, typename Args>
constexpr auto invocable<
F, Args, std::void_t<typename Args::template invoke_result_t<F>>> = true;
} // namespace detail::detect
template <typename F, typename... Args>
constexpr auto invocable =
detail::detect::invocable<F, detail::detect::arg_list<Args...>>;
template <typename F, typename... Args>
constexpr auto predicate =
invocable<F, Args...> and
std::is_convertible_v<std::invoke_result_t<F, Args...>, bool>;
template <typename T> constexpr auto callable = is_callable_v<T>;
template <typename T, template <typename> typename TypeTrait>
constexpr auto has_trait = TypeTrait<T>::value;
#undef DETECTOR
template <typename T> constexpr auto structural = is_structural_v<T>;
#else
// After C++20, we can define concepts that are lacking in the library
template <typename T>
concept integral = std::is_integral_v<T>;
template <typename T>
concept floating_point = std::is_floating_point_v<T>;
template <typename T>
concept signed_integral = integral<T> and std::is_signed_v<T>;
template <typename T>
concept unsigned_integral = integral<T> and std::is_unsigned_v<T>;
template <typename From, typename To>
concept convertible_to = std::is_convertible_v<From, To> and
requires { static_cast<To>(std::declval<From>()); };
template <typename T, typename U>
concept derived_from =
std::is_base_of_v<U, T> and
std::is_convertible_v<T const volatile *, U const volatile *>;
template <typename T, typename U>
concept same_as = std::is_same_v<T, U> and std::is_same_v<U, T>;
template <typename T, typename U>
concept same_as_unqualified =
is_same_unqualified_v<T, U> and is_same_unqualified_v<U, T>;
template <typename T>
concept equality_comparable = requires(T const &t) {
{ t == t } -> same_as<bool>;
{ t != t } -> same_as<bool>;
};
namespace detail {
template <typename T>
concept partially_ordered = requires(T const &t) {
{ t < t } -> same_as<bool>;
{ t <= t } -> same_as<bool>;
{ t > t } -> same_as<bool>;
{ t >= t } -> same_as<bool>;
};
} // namespace detail
template <typename T>
concept totally_ordered =
equality_comparable<T> and detail::partially_ordered<T>;
template <typename F, typename... Args>
concept invocable = requires(F &&f, Args &&...args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
};
namespace detail {
template <typename B>
concept boolean_testable_impl = stdx::convertible_to<B, bool>;
template <typename B>
concept boolean_testable = boolean_testable_impl<B> and requires(B &&b) {
{ not std::forward<B>(b) } -> boolean_testable_impl;
};
} // namespace detail
template <typename F, typename... Args>
concept predicate = invocable<F, Args...> and
detail::boolean_testable<std::invoke_result_t<F, Args...>>;
template <typename T>
concept callable = is_callable_v<T>;
template <typename T, template <typename> typename TypeTrait>
concept has_trait = TypeTrait<T>::value;
template <typename T>
concept structural = is_structural_v<T>;
#endif
} // namespace v1
} // namespace stdx
#else
// C++20 concept library exists, so use that
namespace stdx {
inline namespace v1 {
using std::floating_point;
using std::integral;
using std::signed_integral;
using std::unsigned_integral;
using std::convertible_to;
using std::derived_from;
using std::same_as;
using std::equality_comparable;
using std::totally_ordered;
using std::invocable;
using std::predicate;
template <typename T>
concept callable = is_callable_v<T>;
template <typename T, template <typename> typename TypeTrait>
concept has_trait = TypeTrait<T>::value;
template <typename T, typename U>
concept same_as_unqualified =
is_same_unqualified_v<T, U> and is_same_unqualified_v<U, T>;
template <typename T>
concept structural = is_structural_v<T>;
} // namespace v1
} // namespace stdx
#endif

View File

@@ -0,0 +1,48 @@
#pragma once
#include <stdx/compiler.hpp>
#include <cstddef>
#include <string_view>
namespace stdx {
inline namespace v1 {
template <typename Tag>
CONSTEVAL static auto type_as_string() -> std::string_view {
#if defined(__clang__)
constexpr std::string_view function_name = __PRETTY_FUNCTION__;
constexpr auto rhs = function_name.size() - 2;
#elif defined(__GNUC__) || defined(__GNUG__)
constexpr std::string_view function_name = __PRETTY_FUNCTION__;
constexpr auto rhs = function_name.size() - 51;
#else
static_assert(false, "Unknown compiler, can't build type name.");
#endif
constexpr auto lhs = function_name.rfind('=', rhs) + 2;
return function_name.substr(lhs, rhs - lhs + 1);
}
template <auto Value>
CONSTEVAL static auto enum_as_string() -> std::basic_string_view<char> {
#if defined(__clang__)
constexpr std::string_view value_string = __PRETTY_FUNCTION__;
constexpr auto rhs = value_string.size() - 2;
#elif defined(__GNUC__) || defined(__GNUG__)
constexpr std::string_view value_string = __PRETTY_FUNCTION__;
constexpr auto rhs = value_string.size() - 2;
#else
static_assert(false, "Unknown compiler, can't build type name.");
#endif
constexpr auto lhs = [&]() -> std::string_view::size_type {
if (auto const colon_pos = value_string.find_last_of(':');
colon_pos != std::string_view::npos) {
return colon_pos + 1;
}
return 0;
}();
return value_string.substr(lhs, rhs - lhs + 1);
}
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,237 @@
#pragma once
#if __cplusplus >= 202002L
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/ct_conversions.hpp>
#include <stdx/ct_string.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>
#include <stdx/utility.hpp>
#include <fmt/compile.h>
#include <fmt/format.h>
#include <algorithm>
#include <array>
#include <iterator>
#include <string_view>
#include <utility>
template <std::size_t N>
struct fmt::formatter<stdx::ct_string<N>> : fmt::formatter<std::string_view> {
template <typename Ctx>
constexpr auto format(stdx::ct_string<N> const &s, Ctx &ctx) const {
return fmt::formatter<std::string_view>::format(std::string_view{s},
ctx);
}
};
namespace stdx {
inline namespace v1 {
template <typename Str, typename Args> struct format_result {
[[no_unique_address]] Str str;
[[no_unique_address]] Args args{};
private:
friend constexpr auto operator==(format_result const &,
format_result const &) -> bool = default;
};
template <typename Str, typename Args>
format_result(Str, Args) -> format_result<Str, Args>;
template <typename Str> format_result(Str) -> format_result<Str, tuple<>>;
inline namespace literals {
inline namespace ct_string_literals {
template <ct_string S> CONSTEVAL auto operator""_fmt_res() {
return format_result{cts_t<S>{}};
}
} // namespace ct_string_literals
} // namespace literals
namespace detail {
template <typename It> CONSTEVAL auto find_spec(It first, It last) -> It {
for (auto spec_start = std::find(first, last, '{'); spec_start != last;
spec_start = std::find(spec_start, last, '{')) {
if (spec_start + 1 != last) {
if (*std::next(spec_start) != '{') {
return spec_start;
}
++spec_start;
}
++spec_start;
}
return last;
}
CONSTEVAL auto count_specifiers(std::string_view fmt) -> std::size_t {
auto count = std::size_t{};
for (auto spec_start = find_spec(fmt.begin(), fmt.end());
spec_start != fmt.end();
spec_start = find_spec(++spec_start, fmt.end())) {
++count;
}
return count;
}
template <std::size_t N> CONSTEVAL auto split_specifiers(std::string_view fmt) {
auto splits = std::array<std::string_view, N>{};
auto count = std::size_t{};
auto split_start = fmt.begin();
auto spec_start = find_spec(fmt.begin(), fmt.end());
while (spec_start != fmt.end()) {
auto split_end = std::find(spec_start, fmt.end(), '}');
if (split_end != fmt.end()) {
++split_end;
}
splits[count++] = std::string_view{split_start, split_end};
split_start = split_end;
spec_start = find_spec(split_start, fmt.end());
}
splits[count++] = std::string_view{split_start, spec_start};
return splits;
}
template <typename T>
concept cx_value = requires { typename T::cx_value_t; } or
requires(T t) { ct_string_from_type(t); };
template <typename T, T V>
CONSTEVAL auto arg_value(std::integral_constant<T, V>) {
if constexpr (std::is_enum_v<T>) {
return enum_as_string<V>();
} else {
return V;
}
}
template <typename T> CONSTEVAL auto arg_value(type_identity<T>) {
return type_as_string<T>();
}
template <ct_string S> CONSTEVAL auto arg_value(cts_t<S>) { return S; }
CONSTEVAL auto arg_value(cx_value auto a) {
if constexpr (requires { ct_string_from_type(a); }) {
return ct_string_from_type(a);
} else if constexpr (std::is_enum_v<decltype(a())>) {
return enum_as_string<a()>();
} else if constexpr (requires { arg_value(a()); }) {
return arg_value(a());
} else {
return a();
}
}
template <typename T, typename U, typename S>
constexpr auto operator+(format_result<T, U> r, S s) {
return format_result{r.str + s, r.args};
}
template <typename S, typename T, typename U>
constexpr auto operator+(S s, format_result<T, U> r) {
return format_result{s + r.str, r.args};
}
template <typename A, typename B, typename T, typename U>
constexpr auto operator+(format_result<A, B> r1, format_result<T, U> r2) {
return format_result{r1.str + r2.str, tuple_cat(r1.args, r2.args)};
}
template <typename T, T...> struct null_output;
template <std::size_t Sz> CONSTEVAL auto to_ct_string(std::string_view s) {
return ct_string<Sz + 1>{s.data(), s.size()};
}
CONSTEVAL auto convert_input(auto s) {
if constexpr (requires { ct_string_from_type(s); }) {
return ct_string_from_type(s);
} else {
return s.value;
}
}
template <ct_string S,
template <typename T, T...> typename Output = detail::null_output>
CONSTEVAL auto convert_output() {
if constexpr (same_as<Output<char>, null_output<char>>) {
return cts_t<S>{};
} else {
return ct_string_to_type<S, Output>();
}
}
template <ct_string Fmt, typename Arg> constexpr auto format1(Arg arg) {
if constexpr (requires { arg_value(arg); }) {
constexpr auto fmtstr = FMT_COMPILE(std::string_view{Fmt});
constexpr auto a = arg_value(arg);
auto const f = []<std::size_t N>(auto s, auto v) {
ct_string<N + 1> cts{};
fmt::format_to(cts.begin(), s, v);
return cts;
};
if constexpr (is_specialization_of_v<std::remove_cv_t<decltype(a)>,
format_result>) {
constexpr auto s = convert_input(a.str);
constexpr auto sz = fmt::formatted_size(fmtstr, s);
constexpr auto cts = f.template operator()<sz>(fmtstr, s);
return format_result{cts_t<cts>{}, a.args};
} else {
constexpr auto sz = fmt::formatted_size(fmtstr, a);
constexpr auto cts = f.template operator()<sz>(fmtstr, a);
return format_result{cts_t<cts>{}};
}
} else if constexpr (is_specialization_of_v<Arg, format_result>) {
auto const sub_result = format1<Fmt>(arg.str);
return format_result{sub_result.str, arg.args};
} else {
return format_result{cts_t<Fmt>{}, tuple{arg}};
}
}
template <template <typename T, T...> typename Output>
concept ct_format_compatible = requires {
{
Output<char, 'A'>{} + Output<char, 'B'>{}
} -> same_as<Output<char, 'A', 'B'>>;
};
template <ct_string Fmt> struct fmt_data {
constexpr static auto fmt = std::string_view{Fmt};
constexpr static auto N = count_specifiers(fmt);
constexpr static auto splits = split_specifiers<N + 1>(fmt);
constexpr static auto last_cts = to_ct_string<splits[N].size()>(splits[N]);
};
} // namespace detail
template <ct_string Fmt,
template <typename T, T...> typename Output = detail::null_output>
constexpr auto ct_format = [](auto &&...args) {
if constexpr (not same_as<Output<char>, detail::null_output<char>>) {
static_assert(detail::ct_format_compatible<Output>);
}
using data = detail::fmt_data<Fmt>;
[[maybe_unused]] auto const format1 = [&]<std::size_t I>(auto &&arg) {
constexpr auto cts =
detail::to_ct_string<data::splits[I].size()>(data::splits[I]);
return detail::format1<cts>(FWD(arg));
};
auto const result = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return (format1.template operator()<Is>(FWD(args)) + ... +
format_result{cts_t<data::last_cts>{}});
}(std::make_index_sequence<data::N>{});
return format_result{detail::convert_output<result.str.value, Output>(),
result.args};
};
} // namespace v1
} // namespace stdx
#endif

View File

@@ -0,0 +1,148 @@
#pragma once
#if __cplusplus >= 202002L
#include <stdx/compiler.hpp>
#include <stdx/utility.hpp>
#include <array>
#include <cstddef>
#include <string_view>
#include <utility>
namespace stdx {
inline namespace v1 {
template <std::size_t N> struct ct_string {
CONSTEVAL ct_string() = default;
// NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor)
CONSTEVAL explicit(false) ct_string(char const (&str)[N]) {
for (auto i = std::size_t{}; i < N; ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-*)
value[i] = str[i];
}
}
CONSTEVAL explicit(true) ct_string(char const *str, std::size_t sz) {
for (auto i = std::size_t{}; i < sz; ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-*)
value[i] = str[i];
}
}
CONSTEVAL explicit(true) ct_string(std::string_view str)
: ct_string{str.data(), str.size()} {}
[[nodiscard]] constexpr auto begin() LIFETIMEBOUND { return value.begin(); }
[[nodiscard]] constexpr auto end() LIFETIMEBOUND { return value.end() - 1; }
[[nodiscard]] constexpr auto begin() const LIFETIMEBOUND {
return value.begin();
}
[[nodiscard]] constexpr auto end() const LIFETIMEBOUND {
return value.end() - 1;
}
[[nodiscard]] constexpr auto rbegin() const LIFETIMEBOUND {
return ++value.rbegin();
}
[[nodiscard]] constexpr auto rend() const LIFETIMEBOUND {
return value.rend();
}
constexpr static std::integral_constant<std::size_t, N> capacity{};
constexpr static std::integral_constant<std::size_t, N - 1U> size{};
constexpr static std::integral_constant<bool, N == 1U> empty{};
constexpr explicit(true) operator std::string_view() const {
return std::string_view{value.data(), size()};
}
std::array<char, N> value{};
};
template <std::size_t N, std::size_t M>
[[nodiscard]] constexpr auto operator==(ct_string<N> const &lhs,
ct_string<M> const &rhs) -> bool {
return static_cast<std::string_view>(lhs) ==
static_cast<std::string_view>(rhs);
}
template <template <typename C, C...> typename T, char... Cs>
[[nodiscard]] CONSTEVAL auto ct_string_from_type(T<char, Cs...>) {
return ct_string<sizeof...(Cs) + 1U>{{Cs..., 0}};
}
template <ct_string S, template <typename C, C...> typename T>
[[nodiscard]] CONSTEVAL auto ct_string_to_type() {
return [&]<auto... Is>(std::index_sequence<Is...>) {
return T<char, std::get<Is>(S.value)...>{};
}(std::make_index_sequence<S.size()>{});
}
template <ct_string S, char C> [[nodiscard]] consteval auto split() {
constexpr auto it = [] {
for (auto i = S.value.cbegin(); i != S.value.cend(); ++i) {
if (*i == C) {
return i;
}
}
return S.value.cend();
}();
if constexpr (it == S.value.cend()) {
return std::pair{S, ct_string{""}};
} else {
constexpr auto prefix_size =
static_cast<std::size_t>(it - S.value.cbegin());
constexpr auto suffix_size = S.size() - prefix_size;
return std::pair{
ct_string<prefix_size + 1U>{S.value.cbegin(), prefix_size},
ct_string<suffix_size>{it + 1, suffix_size - 1U}};
}
}
template <std::size_t N, std::size_t M>
[[nodiscard]] constexpr auto
operator+(ct_string<N> const &lhs,
ct_string<M> const &rhs) -> ct_string<N + M - 1> {
ct_string<N + M - 1> ret{};
for (auto i = std::size_t{}; i < lhs.size(); ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-*)
ret.value[i] = lhs.value[i];
}
for (auto i = std::size_t{}; i < rhs.size(); ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-*)
ret.value[i + N - 1] = rhs.value[i];
}
return ret;
}
template <ct_string S> struct cts_t {
constexpr static auto value = S;
};
template <ct_string X, ct_string Y>
constexpr auto operator==(cts_t<X>, cts_t<Y>) -> bool {
return X == Y;
}
template <ct_string X, ct_string Y>
constexpr auto operator+(cts_t<X>, cts_t<Y>) {
return cts_t<X + Y>{};
}
namespace detail {
template <std::size_t N> struct ct_helper<ct_string<N>>;
} // namespace detail
template <ct_string Value> CONSTEVAL auto ct() { return cts_t<Value>{}; }
inline namespace literals {
inline namespace ct_string_literals {
template <ct_string S> CONSTEVAL auto operator""_cts() { return S; }
template <ct_string S> CONSTEVAL auto operator""_ctst() { return cts_t<S>{}; }
} // namespace ct_string_literals
} // namespace literals
} // namespace v1
} // namespace stdx
#endif

View File

@@ -0,0 +1,143 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/iterator.hpp>
#include <stdx/utility.hpp>
#include <array>
#include <cstddef>
#include <iterator>
#include <type_traits>
namespace stdx {
inline namespace v1 {
template <typename Key, typename Value> struct cx_map_value {
using key_type = Key;
using mapped_type = Value;
key_type key{};
mapped_type value{};
};
template <typename K, typename V> cx_map_value(K, V) -> cx_map_value<K, V>;
template <typename Key, typename Value, std::size_t N> class cx_map {
public:
using value_type = cx_map_value<Key, Value>;
using key_type = typename value_type::key_type;
using mapped_type = typename value_type::mapped_type;
using size_type = std::size_t;
using reference = value_type &;
using const_reference = value_type const &;
using iterator = value_type *;
using const_iterator = value_type const *;
private:
std::array<value_type, N> storage{};
std::size_t current_size{};
public:
constexpr cx_map() = default;
// NOLINTNEXTLINE(modernize-use-constraints)
template <typename... Vs, std::enable_if_t<((sizeof...(Vs) <= N) and ... and
stdx::same_as<value_type, Vs>),
int> = 0>
constexpr explicit cx_map(Vs const &...vs)
: storage{vs...}, current_size{sizeof...(Vs)} {}
[[nodiscard]] constexpr auto begin() LIFETIMEBOUND -> iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto begin() const LIFETIMEBOUND -> const_iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto
cbegin() const LIFETIMEBOUND -> const_iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto end() LIFETIMEBOUND -> iterator {
return begin() + current_size;
}
[[nodiscard]] constexpr auto end() const LIFETIMEBOUND -> const_iterator {
return begin() + current_size;
}
[[nodiscard]] constexpr auto cend() const LIFETIMEBOUND -> const_iterator {
return cbegin() + current_size;
}
[[nodiscard]] constexpr auto size() const -> std::size_t {
return current_size;
}
constexpr static std::integral_constant<size_type, N> capacity{};
[[nodiscard]] constexpr auto full() const -> bool {
return current_size == N;
}
[[nodiscard]] constexpr auto empty() const -> bool {
return current_size == 0;
}
constexpr auto clear() -> void { current_size = 0; }
[[nodiscard]] constexpr auto pop_back() -> value_type {
return storage[--current_size];
}
[[nodiscard]] constexpr auto
get(key_type const &key) LIFETIMEBOUND -> mapped_type & {
for (auto &[k, v] : *this) {
if (k == key) {
return v;
}
}
unreachable();
}
[[nodiscard]] constexpr auto
get(key_type const &key) const LIFETIMEBOUND -> mapped_type const & {
for (auto const &[k, v] : *this) {
if (k == key) {
return v;
}
}
unreachable();
}
[[nodiscard]] constexpr auto contains(key_type const &key) const -> bool {
for (auto const &[k, v] : *this) {
if (k == key) {
return true;
}
}
return false;
}
constexpr auto insert_or_assign(key_type const &key,
mapped_type const &value) -> bool {
for (auto &[k, v] : *this) {
if (k == key) {
v = value;
return false;
}
}
storage[current_size++] = {key, value};
return true;
}
constexpr auto put(key_type const &key, mapped_type const &value) -> bool {
return insert_or_assign(key, value);
}
constexpr auto erase(key_type const &key) -> size_type {
for (auto &v : *this) {
if (v.key == key) {
v = storage[--current_size];
return 1u;
}
}
return 0u;
}
};
template <typename K, typename V, std::size_t N>
constexpr auto ct_capacity_v<cx_map<K, V, N>> = N;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,112 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/cx_map.hpp>
#include <stdx/cx_set.hpp>
#include <stdx/iterator.hpp>
#include <cstddef>
#include <iterator>
#include <type_traits>
namespace stdx {
inline namespace v1 {
template <typename Key, typename Value, std::size_t KeyN,
std::size_t ValueN = KeyN>
class cx_multimap {
using set_t = cx_set<Value, ValueN>;
using storage_t = cx_map<Key, set_t, KeyN>;
storage_t storage{};
public:
using key_type = Key;
using mapped_type = Value;
using size_type = std::size_t;
using iterator = typename storage_t::iterator;
using const_iterator = typename storage_t::const_iterator;
[[nodiscard]] constexpr auto begin() LIFETIMEBOUND -> iterator {
return std::begin(storage);
}
[[nodiscard]] constexpr auto begin() const LIFETIMEBOUND -> const_iterator {
return std::begin(storage);
}
[[nodiscard]] constexpr auto
cbegin() const LIFETIMEBOUND -> const_iterator {
return std::cbegin(storage);
}
[[nodiscard]] constexpr auto end() LIFETIMEBOUND -> iterator {
return std::end(storage);
}
[[nodiscard]] constexpr auto end() const LIFETIMEBOUND -> const_iterator {
return std::end(storage);
}
[[nodiscard]] constexpr auto cend() const LIFETIMEBOUND -> const_iterator {
return std::cend(storage);
}
[[nodiscard]] constexpr auto size() const -> std::size_t {
return std::size(storage);
}
constexpr static std::integral_constant<size_type, KeyN> capacity{};
constexpr auto insert(key_type const &k, mapped_type const &v) -> void {
if (storage.contains(k)) {
storage.get(k).insert(v);
} else {
storage.insert_or_assign(k, set_t{v});
}
}
constexpr auto insert(key_type const &k) -> void {
if (not storage.contains(k)) {
storage.insert_or_assign(k, set_t{});
}
}
template <typename... Args> constexpr auto put(Args &&...args) -> void {
return insert(std::forward<Args>(args)...);
}
constexpr auto erase(key_type const &key) -> size_type {
return storage.erase(key);
}
constexpr auto erase(key_type const &k, mapped_type const &v) -> size_type {
if (storage.contains(k)) {
auto &s = storage.get(k);
auto const r = s.erase(v);
if (s.empty()) {
storage.erase(k);
}
return r;
}
return 0;
}
[[nodiscard]] constexpr auto
get(key_type const &key) LIFETIMEBOUND -> set_t & {
return storage.get(key);
}
[[nodiscard]] constexpr auto
get(key_type const &key) const LIFETIMEBOUND -> set_t const & {
return storage.get(key);
}
[[nodiscard]] constexpr auto empty() const -> bool {
return storage.empty();
}
[[nodiscard]] constexpr auto contains(key_type const &key) const -> bool {
return storage.contains(key);
}
[[nodiscard]] constexpr auto contains(key_type const &k,
mapped_type const &v) const -> bool {
return contains(k) and get(k).contains(v);
}
constexpr auto clear() -> void { storage.clear(); }
};
template <typename K, typename V, std::size_t N, std::size_t M>
constexpr auto ct_capacity_v<cx_multimap<K, V, N, M>> = N;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,115 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/iterator.hpp>
#include <stdx/panic.hpp>
#include <array>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
struct unsafe_overflow_policy {
template <typename... Args> constexpr static auto check_push(Args &&...) {}
template <typename... Args> constexpr static auto check_pop(Args &&...) {}
};
struct safe_overflow_policy {
constexpr static auto check_push(std::size_t size, std::size_t capacity) {
if (size >= capacity) {
STDX_PANIC("cx_queue overflow!");
}
}
constexpr static auto check_pop(std::size_t size) {
if (size <= 0) {
STDX_PANIC("cx_queue underflow!");
}
}
};
template <typename T, std::size_t N,
typename OverflowPolicy = safe_overflow_policy>
class cx_queue {
std::array<T, N> storage{};
std::size_t push_index{N - 1};
std::size_t pop_index{};
std::size_t current_size{};
public:
using value_type = T;
using size_type = std::size_t;
using reference = value_type &;
using const_reference = value_type const &;
[[nodiscard]] constexpr auto size() const -> size_type {
return current_size;
}
constexpr static std::integral_constant<size_type, N> capacity{};
[[nodiscard]] constexpr auto full() const -> bool {
return current_size == N;
}
[[nodiscard]] constexpr auto empty() const -> bool {
return current_size == 0u;
}
constexpr auto clear() -> void {
pop_index = 0;
push_index = N - 1;
current_size = 0;
}
[[nodiscard]] constexpr auto front() & LIFETIMEBOUND -> reference {
OverflowPolicy::check_pop(current_size);
return storage[pop_index];
}
[[nodiscard]] constexpr auto front() const
& LIFETIMEBOUND -> const_reference {
OverflowPolicy::check_pop(current_size);
return storage[pop_index];
}
[[nodiscard]] constexpr auto back() & LIFETIMEBOUND -> reference {
OverflowPolicy::check_pop(current_size);
return storage[push_index];
}
[[nodiscard]] constexpr auto back() const
& LIFETIMEBOUND -> const_reference {
OverflowPolicy::check_pop(current_size);
return storage[push_index];
}
constexpr auto push(value_type const &value) LIFETIMEBOUND -> reference {
OverflowPolicy::check_push(current_size, N);
if (++push_index == N) {
push_index = 0;
}
++current_size;
return storage[push_index] = value;
}
constexpr auto push(value_type &&value) LIFETIMEBOUND -> reference {
OverflowPolicy::check_push(current_size, N);
if (++push_index == N) {
push_index = 0;
}
++current_size;
return storage[push_index] = std::move(value);
}
[[nodiscard]] constexpr auto pop() -> value_type {
OverflowPolicy::check_pop(current_size);
auto entry = std::move(storage[pop_index++]);
if (pop_index == N) {
pop_index = 0;
}
--current_size;
return entry;
}
};
template <typename T, std::size_t N, typename OP>
constexpr auto ct_capacity_v<cx_queue<T, N, OP>> = N;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,118 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/cx_map.hpp>
#include <stdx/iterator.hpp>
#include <cstddef>
namespace stdx {
inline namespace v1 {
template <typename Key, std::size_t N> class cx_set {
std::array<Key, N> storage{};
std::size_t current_size{};
public:
using key_type = Key;
using value_type = Key;
using size_type = std::size_t;
using reference = value_type &;
using const_reference = value_type const &;
using iterator = value_type *;
using const_iterator = value_type const *;
constexpr cx_set() = default;
template <typename... Ts,
// NOLINTNEXTLINE(modernize-use-constraints)
std::enable_if_t<((sizeof...(Ts) <= N) and ... and
stdx::convertible_to<key_type, Ts>),
int> = 0>
constexpr explicit cx_set(Ts const &...ts)
: storage{static_cast<value_type>(ts)...}, current_size{sizeof...(Ts)} {
}
[[nodiscard]] constexpr auto begin() LIFETIMEBOUND -> iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto begin() const LIFETIMEBOUND -> const_iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto
cbegin() const LIFETIMEBOUND -> const_iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto end() LIFETIMEBOUND -> iterator {
return begin() + current_size;
}
[[nodiscard]] constexpr auto end() const LIFETIMEBOUND -> const_iterator {
return begin() + current_size;
}
[[nodiscard]] constexpr auto cend() const LIFETIMEBOUND -> const_iterator {
return cbegin() + current_size;
}
[[nodiscard]] constexpr auto size() const -> size_type {
return current_size;
}
constexpr static std::integral_constant<size_type, N> capacity{};
[[nodiscard]] constexpr auto contains(key_type const &key) const -> bool {
for (auto const &k : *this) {
if (k == key) {
return true;
}
}
return false;
}
constexpr auto insert(key_type const &key) -> bool {
if (contains(key)) {
return false;
}
storage[current_size++] = key;
return true;
}
constexpr auto insert(key_type &&key) -> bool {
if (contains(key)) {
return false;
}
storage[current_size++] = std::move(key);
return true;
}
constexpr auto erase(key_type const &key) -> size_type {
for (auto &k : *this) {
if (k == key) {
k = storage[--current_size];
return 1u;
}
}
return 0u;
}
template <typename Set> constexpr auto merge(Set s) -> void {
for (auto const &entry : s) {
insert(entry);
}
}
[[nodiscard]] constexpr auto empty() const -> bool {
return current_size == 0u;
}
constexpr auto clear() -> void { current_size = 0; }
[[nodiscard]] constexpr auto pop_back() -> key_type {
return storage[--current_size];
}
};
template <typename T, typename... Ts>
cx_set(T, Ts...) -> cx_set<T, 1 + sizeof...(Ts)>;
template <typename T, std::size_t N>
constexpr auto ct_capacity_v<cx_set<T, N>> = N;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,189 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/iterator.hpp>
#include <array>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <typename T, std::size_t N> class cx_vector {
std::array<T, N> storage{};
std::size_t current_size{};
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type &;
using const_reference = value_type const &;
using pointer = value_type *;
using const_pointer = value_type const *;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr cx_vector() = default;
template <typename... Ts,
// NOLINTNEXTLINE(modernize-use-constraints)
std::enable_if_t<((sizeof...(Ts) <= N) and ... and
stdx::convertible_to<value_type, Ts>),
int> = 0>
constexpr explicit cx_vector(Ts const &...ts)
: storage{static_cast<value_type>(ts)...}, current_size{sizeof...(Ts)} {
}
[[nodiscard]] constexpr auto begin() LIFETIMEBOUND -> iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto begin() const LIFETIMEBOUND -> const_iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto
cbegin() const LIFETIMEBOUND -> const_iterator {
return std::data(storage);
}
[[nodiscard]] constexpr auto end() LIFETIMEBOUND -> iterator {
return begin() + current_size;
}
[[nodiscard]] constexpr auto end() const LIFETIMEBOUND -> const_iterator {
return begin() + current_size;
}
[[nodiscard]] constexpr auto cend() const LIFETIMEBOUND -> const_iterator {
return cbegin() + current_size;
}
[[nodiscard]] constexpr auto rbegin() LIFETIMEBOUND -> reverse_iterator {
return end();
}
[[nodiscard]] constexpr auto
rbegin() const LIFETIMEBOUND -> const_reverse_iterator {
return end();
}
[[nodiscard]] constexpr auto
crbegin() const LIFETIMEBOUND -> const_reverse_iterator {
return cend();
}
[[nodiscard]] constexpr auto rend() LIFETIMEBOUND -> reverse_iterator {
return begin();
}
[[nodiscard]] constexpr auto
rend() const LIFETIMEBOUND -> const_reverse_iterator {
return begin();
}
[[nodiscard]] constexpr auto
crend() const LIFETIMEBOUND -> const_reverse_iterator {
return cbegin();
}
[[nodiscard]] constexpr auto front() LIFETIMEBOUND -> reference {
return storage[0];
}
[[nodiscard]] constexpr auto
front() const LIFETIMEBOUND -> const_reference {
return storage[0];
}
[[nodiscard]] constexpr auto back() LIFETIMEBOUND -> reference {
return storage[current_size - 1];
}
[[nodiscard]] constexpr auto back() const LIFETIMEBOUND -> const_reference {
return storage[current_size - 1];
}
[[nodiscard]] constexpr auto size() const -> size_type {
return current_size;
}
constexpr static std::integral_constant<size_type, N> capacity{};
[[nodiscard]] constexpr auto
operator[](std::size_t index) LIFETIMEBOUND->reference {
return storage[index];
}
[[nodiscard]] constexpr auto
operator[](std::size_t index) const LIFETIMEBOUND->const_reference {
return storage[index];
}
template <std::size_t Index>
[[nodiscard]] constexpr auto get() LIFETIMEBOUND -> reference {
return std::get<Index>(storage);
}
template <std::size_t Index>
[[nodiscard]] constexpr auto get() const LIFETIMEBOUND -> const_reference {
return std::get<Index>(storage);
}
[[nodiscard]] constexpr auto full() const -> bool {
return current_size == N;
}
[[nodiscard]] constexpr auto empty() const -> bool {
return current_size == 0u;
}
constexpr auto clear() -> void { current_size = 0; }
constexpr auto
push_back(value_type const &value) LIFETIMEBOUND -> reference {
return storage[current_size++] = value;
}
constexpr auto push_back(value_type &&value) LIFETIMEBOUND -> reference {
return storage[current_size++] = std::move(value);
}
[[nodiscard]] constexpr auto pop_back() -> value_type {
return storage[--current_size];
}
private:
template <typename F>
friend constexpr auto resize_and_overwrite(cx_vector &v, F &&f) -> void {
v.current_size =
std::forward<F>(f)(std::data(v.storage), std::size(v.storage));
}
[[nodiscard]] friend constexpr auto
operator==(cx_vector const &lhs, cx_vector const &rhs) -> bool {
if (lhs.size() != rhs.size()) {
return false;
}
for (auto i = size_type{}; i < lhs.size(); ++i) {
if (lhs[i] != rhs[i]) {
return false;
}
}
return true;
}
#if __cpp_impl_three_way_comparison < 201907L
[[nodiscard]] friend constexpr auto
operator!=(cx_vector const &lhs, cx_vector const &rhs) -> bool {
return not(lhs == rhs);
}
#endif
};
template <typename T, typename... Ts>
cx_vector(T, Ts...) -> cx_vector<T, 1 + sizeof...(Ts)>;
template <std::size_t I, typename T, std::size_t N>
auto get(cx_vector<T, N> &v LIFETIMEBOUND) -> decltype(auto) {
return v.template get<I>();
}
template <std::size_t I, typename T, std::size_t N>
auto get(cx_vector<T, N> const &v LIFETIMEBOUND) -> decltype(auto) {
return v.template get<I>();
}
template <typename T, std::size_t N>
constexpr auto ct_capacity_v<cx_vector<T, N>> = N;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,10 @@
#pragma once
namespace stdx {
inline namespace v1 {
struct place_bits_t {};
constexpr inline auto place_bits = place_bits_t{};
struct all_bits_t {};
constexpr inline auto all_bits = all_bits_t{};
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,123 @@
#pragma once
#include <stdx/concepts.hpp>
#include <stdx/panic.hpp>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
#if __cpp_concepts >= 201907L
namespace detail {
template <typename T>
concept base_single_linkable = requires(T node) {
{ node->next } -> same_as<T &>;
};
template <typename T>
concept base_double_linkable = base_single_linkable<T> and requires(T node) {
{ node->prev } -> same_as<T &>;
};
} // namespace detail
template <typename T>
concept single_linkable = requires(T *node) {
requires detail::base_single_linkable<
std::remove_cvref_t<decltype(node->next)>>;
};
template <typename T>
concept double_linkable = requires(T *node) {
requires detail::base_double_linkable<
std::remove_cvref_t<decltype(node->next)>>;
requires detail::base_double_linkable<
std::remove_cvref_t<decltype(node->prev)>>;
};
#define STDX_SINGLE_LINKABLE single_linkable
#define STDX_DOUBLE_LINKABLE double_linkable
#else
#define STDX_SINGLE_LINKABLE typename
#define STDX_DOUBLE_LINKABLE typename
#endif
namespace detail::detect {
template <typename T, typename = void> constexpr auto has_prev_pointer = false;
template <typename T>
constexpr auto
has_prev_pointer<T, std::void_t<decltype(std::declval<T>().prev)>> = true;
} // namespace detail::detect
namespace node_policy {
template <typename Node> class checked {
constexpr static auto valid_for_push(Node *node) -> bool {
if constexpr (detail::detect::has_prev_pointer<Node>) {
return node->prev == nullptr and node->next == nullptr;
} else {
return node->next == nullptr;
}
}
public:
template <typename L>
constexpr static auto push_front(L &list, Node *node) -> void {
if (not valid_for_push(node)) {
STDX_PANIC("bad list node!");
}
list.unchecked_push_front(node);
}
template <typename L>
constexpr static auto push_back(L &list, Node *node) -> void {
if (not valid_for_push(node)) {
STDX_PANIC("bad list node!");
}
list.unchecked_push_back(node);
}
template <typename L, typename It>
constexpr static auto insert(L &list, It it, Node *node) -> void {
if (not valid_for_push(node)) {
STDX_PANIC("bad list node!");
}
list.unchecked_insert(it, node);
}
constexpr static auto on_pop(Node *node) {
if constexpr (detail::detect::has_prev_pointer<Node>) {
node->prev = nullptr;
}
node->next = nullptr;
}
constexpr static auto on_clear(Node *head) {
while (head != nullptr) {
if constexpr (detail::detect::has_prev_pointer<Node>) {
head->prev = nullptr;
}
head = std::exchange(head->next, nullptr);
}
}
};
template <typename Node> struct unchecked {
template <typename L>
constexpr static auto push_front(L &list, Node *node) -> void {
list.unchecked_push_front(node);
}
template <typename L>
constexpr static auto push_back(L &list, Node *node) -> void {
list.unchecked_push_back(node);
}
template <typename L, typename It>
constexpr static auto insert(L &list, It it, Node *node) -> void {
list.unchecked_insert(it, node);
}
constexpr static auto on_pop(Node *) {}
constexpr static auto on_clear(Node *) {}
};
} // namespace node_policy
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,64 @@
#pragma once
#include <stdx/function_traits.hpp>
#if __cplusplus >= 202002L
#include <stdx/tuple.hpp>
#define TUPLE_T stdx::tuple
#define TUPLE_GET stdx::get
#else
#include <tuple>
#define TUPLE_T std::tuple
#define TUPLE_GET std::get
#endif
#include <cstddef>
#include <functional>
#include <utility>
namespace stdx {
inline namespace v1 {
namespace detail {
template <typename, typename> struct for_each_n_args;
template <std::size_t... Rows, std::size_t... Columns>
struct for_each_n_args<std::index_sequence<Rows...>,
std::index_sequence<Columns...>> {
template <typename F, typename T> static auto apply(F &&f, T &&t) -> void {
(exec<Rows * sizeof...(Columns)>(f, std::forward<T>(t)), ...);
}
private:
template <std::size_t RowIndex, typename F, typename T>
static auto exec(F &&f, T &&t) -> void {
std::invoke(f, TUPLE_GET<RowIndex + Columns>(std::forward<T>(t))...);
}
};
} // namespace detail
template <std::size_t N = 0, typename F, typename... Args>
void for_each_n_args(F &&f, Args &&...args) {
constexpr auto batch_size = [] {
if constexpr (N == 0) {
return arity_t<F>::value;
} else {
return N;
}
}();
static_assert(sizeof...(Args) % batch_size == 0,
"for_each_n_args: number of args must be a multiple of the "
"given N (or function arity)");
using tuple_t = TUPLE_T<Args &&...>;
detail::for_each_n_args<
std::make_index_sequence<sizeof...(Args) / batch_size>,
std::make_index_sequence<batch_size>>::apply(std::forward<F>(f),
tuple_t{std::forward<Args>(
args)...});
}
} // namespace v1
} // namespace stdx
#undef TUPLE_T
#undef TUPLE_GET

View File

@@ -0,0 +1,85 @@
#pragma once
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/utility.hpp>
#include <functional>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
namespace detail {
template <typename...> struct function_traits;
template <typename R, typename... Args>
struct function_traits<std::function<R(Args...)>> {
using return_type = R;
template <template <typename...> typename List> using args = List<Args...>;
template <template <typename...> typename List>
using decayed_args = List<std::decay_t<Args>...>;
using arity = std::integral_constant<std::size_t, sizeof...(Args)>;
template <auto N>
using nth_arg = boost::mp11::mp_at_c<args<boost::mp11::mp_list>, N>;
template <auto N>
using decayed_nth_arg =
boost::mp11::mp_at_c<decayed_args<boost::mp11::mp_list>, N>;
};
} // namespace detail
template <typename F>
using function_traits =
detail::function_traits<decltype(std::function{std::declval<F>()})>;
template <typename F> using return_t = typename function_traits<F>::return_type;
template <typename F, template <typename...> typename List>
using args_t = typename function_traits<F>::template args<List>;
template <typename F, template <typename...> typename List>
using decayed_args_t = typename function_traits<F>::template decayed_args<List>;
template <typename F>
using nongeneric_arity_t = typename function_traits<F>::arity;
template <typename F, auto N>
using nth_arg_t = typename function_traits<F>::template nth_arg<N>;
template <typename F, auto N>
using decayed_nth_arg_t =
typename function_traits<F>::template decayed_nth_arg<N>;
namespace detail {
template <auto> struct any_type {
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename T> operator T();
};
template <typename F, std::size_t... Is>
constexpr auto try_invoke_impl(std::index_sequence<Is...>)
-> std::invoke_result_t<F, any_type<Is>...>;
template <typename F, typename N>
using try_invoke =
decltype(try_invoke_impl<F>(std::make_index_sequence<N::value>{}));
template <typename F, typename N>
using has_arg_count = boost::mp11::mp_valid<try_invoke, F, N>;
template <typename F, typename N> struct generic_arity;
template <typename F, typename N>
using generic_arity_t = typename generic_arity<F, N>::type;
template <typename F, typename N> struct generic_arity {
using type = boost::mp11::mp_eval_if<
has_arg_count<F, N>, N, generic_arity_t, F,
std::integral_constant<std::size_t, N::value + 1u>>;
};
} // namespace detail
template <typename F>
using arity_t = boost::mp11::mp_eval_or<
detail::generic_arity_t<F, std::integral_constant<std::size_t, 0u>>,
nongeneric_arity_t, F>;
template <typename F> constexpr auto arity_v = arity_t<F>::value;
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,174 @@
#pragma once
#include <stdx/type_traits.hpp>
#include <functional>
#include <type_traits>
#if __cpp_lib_bind_front < 202306L or __cpp_lib_bind_back < 202306L
#if __cplusplus >= 202002L
#include <stdx/tuple.hpp>
#else
#include <tuple>
#endif
#endif
namespace stdx {
inline namespace v1 {
template <typename F> struct with_result_of : F {
using R = std::invoke_result_t<F>;
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator R() const
noexcept(noexcept(static_cast<F const &>(*this)())) {
return static_cast<F const &>(*this)();
}
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator R() noexcept(noexcept(static_cast<F &>(*this)())) {
return static_cast<F &>(*this)();
}
};
#if __cpp_deduction_guides < 201907L
template <typename F> with_result_of(F) -> with_result_of<F>;
#endif
namespace detail {
#if __cpp_lib_bind_front < 202306L or __cpp_lib_bind_back < 202306L
#if __cplusplus >= 202002L
template <typename... Ts> using bind_tuple_t = stdx::tuple<Ts...>;
using stdx::get;
#else
template <typename... Ts> using bind_tuple_t = std::tuple<Ts...>;
using std::get;
#endif
#endif
} // namespace detail
#if __cpp_lib_bind_front >= 201907L
using std::bind_front;
#else
namespace detail {
template <typename...> struct bind_front_t;
template <typename F, std::size_t... Is, typename... BoundArgs>
struct bind_front_t<F, std::index_sequence<Is...>, BoundArgs...> {
F f{};
bind_tuple_t<BoundArgs...> t{};
template <typename... Args>
constexpr auto operator()(Args &&...args) const & {
return f(get<Is>(t)..., std::forward<Args>(args)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) & {
return f(get<Is>(t)..., std::forward<Args>(args)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) && {
return std::move(f)(get<Is>(std::move(t))...,
std::forward<Args>(args)...);
}
};
} // namespace detail
template <typename F, typename... Args>
constexpr auto bind_front(F &&f, Args &&...args) {
return detail::bind_front_t<stdx::remove_cvref_t<F>,
std::make_index_sequence<sizeof...(Args)>,
std::decay_t<Args>...>{
std::forward<F>(f), {std::forward<Args>(args)...}};
}
#endif
#if __cpp_lib_bind_front < 202306L
namespace detail {
template <auto, typename...> struct bind_front_value_t;
template <auto F, std::size_t... Is, typename... BoundArgs>
struct bind_front_value_t<F, std::index_sequence<Is...>, BoundArgs...> {
bind_tuple_t<BoundArgs...> t{};
template <typename... Args>
constexpr auto operator()(Args &&...args) const & {
return F(get<Is>(t)..., std::forward<Args>(args)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) & {
return F(get<Is>(t)..., std::forward<Args>(args)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) && {
return F(get<Is>(std::move(t))..., std::forward<Args>(args)...);
}
};
} // namespace detail
template <auto F, typename... Args> constexpr auto bind_front(Args &&...args) {
return detail::bind_front_value_t<
F, std::make_index_sequence<sizeof...(Args)>, std::decay_t<Args>...>{
{std::forward<Args>(args)...}};
}
#endif
#if __cpp_lib_bind_back >= 202202L
using std::bind_back;
#else
namespace detail {
template <typename...> struct bind_back_t;
template <typename F, std::size_t... Is, typename... BoundArgs>
struct bind_back_t<F, std::index_sequence<Is...>, BoundArgs...> {
F f{};
bind_tuple_t<BoundArgs...> t{};
template <typename... Args>
constexpr auto operator()(Args &&...args) const & {
return f(std::forward<Args>(args)..., get<Is>(t)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) & {
return f(std::forward<Args>(args)..., get<Is>(t)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) && {
return std::move(f)(std::forward<Args>(args)...,
get<Is>(std::move(t))...);
}
};
} // namespace detail
template <typename F, typename... Args>
constexpr auto bind_back(F &&f, Args &&...args) {
return detail::bind_back_t<stdx::remove_cvref_t<F>,
std::make_index_sequence<sizeof...(Args)>,
std::decay_t<Args>...>{
std::forward<F>(f), {std::forward<Args>(args)...}};
}
#endif
#if __cpp_lib_bind_back < 202306L
namespace detail {
template <auto, typename...> struct bind_back_value_t;
template <auto F, std::size_t... Is, typename... BoundArgs>
struct bind_back_value_t<F, std::index_sequence<Is...>, BoundArgs...> {
bind_tuple_t<BoundArgs...> t{};
template <typename... Args>
constexpr auto operator()(Args &&...args) const & {
return F(std::forward<Args>(args)..., get<Is>(t)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) & {
return F(std::forward<Args>(args)..., get<Is>(t)...);
}
template <typename... Args> constexpr auto operator()(Args &&...args) && {
return F(std::forward<Args>(args)..., get<Is>(std::move(t))...);
}
};
} // namespace detail
template <auto F, typename... Args> constexpr auto bind_back(Args &&...args) {
return detail::bind_back_value_t<
F, std::make_index_sequence<sizeof...(Args)>, std::decay_t<Args>...>{
{std::forward<Args>(args)...}};
}
#endif
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,141 @@
#pragma once
#include <stdx/detail/list_common.hpp>
#include <cstddef>
#if __cplusplus < 202002L
#include <iterator>
#endif
namespace stdx {
inline namespace v1 {
template <STDX_SINGLE_LINKABLE NodeType,
template <typename> typename P = node_policy::checked>
class intrusive_forward_list {
friend P<NodeType>;
template <typename N> struct iterator_t {
using difference_type = std::ptrdiff_t;
using value_type = N;
using pointer = value_type *;
using reference = value_type &;
#if __cplusplus < 202002L
using iterator_category = std::forward_iterator_tag;
#endif
constexpr iterator_t() = default;
constexpr explicit iterator_t(pointer n) : node{n} {}
constexpr auto operator*() -> reference { return *node; }
constexpr auto operator*() const -> reference { return *node; }
constexpr auto operator->() -> pointer { return node; }
constexpr auto operator->() const -> pointer { return node; }
constexpr auto operator++() -> iterator_t & {
node = node->next;
return *this;
}
constexpr auto operator++(int) -> iterator_t {
auto tmp = *this;
++(*this);
return tmp;
}
private:
pointer node{};
#if __cpp_impl_three_way_comparison < 201907L
friend constexpr auto operator==(iterator_t lhs,
iterator_t rhs) -> bool {
return lhs.node == rhs.node;
}
friend constexpr auto operator!=(iterator_t lhs,
iterator_t rhs) -> bool {
return not(lhs == rhs);
}
#else
friend constexpr auto operator==(iterator_t,
iterator_t) -> bool = default;
#endif
};
public:
using value_type = NodeType;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type &;
using const_reference = value_type const &;
using pointer = value_type *;
using const_pointer = value_type const *;
using iterator = iterator_t<value_type>;
using const_iterator = iterator_t<value_type const>;
private:
pointer head{};
pointer tail{};
constexpr auto unchecked_push_front(pointer n) -> void {
n->next = head;
head = n;
if (tail == nullptr) {
tail = n;
}
}
constexpr auto unchecked_push_back(pointer n) -> void {
if (tail != nullptr) {
tail->next = n;
}
tail = n;
n->next = nullptr;
if (head == nullptr) {
head = n;
}
}
public:
constexpr auto begin() -> iterator { return iterator{head}; }
constexpr auto begin() const -> const_iterator {
return const_iterator{head};
}
constexpr auto cbegin() const -> const_iterator {
return const_iterator{head};
}
constexpr auto end() -> iterator { return {}; }
constexpr auto end() const -> const_iterator { return {}; }
constexpr auto cend() const -> const_iterator { return {}; }
constexpr auto front() const -> reference { return *head; }
constexpr auto back() const -> reference { return *tail; }
constexpr auto push_front(pointer n) -> void {
P<NodeType>::push_front(*this, n);
}
constexpr auto push_back(pointer n) -> void {
P<NodeType>::push_back(*this, n);
}
constexpr auto pop_front() -> pointer {
pointer poppedNode = head;
head = head->next;
if (head == nullptr) {
tail = nullptr;
}
P<NodeType>::on_pop(poppedNode);
return poppedNode;
}
[[nodiscard]] constexpr auto empty() const -> bool {
return head == nullptr;
}
constexpr auto clear() -> void {
P<NodeType>::on_clear(head);
head = nullptr;
tail = nullptr;
}
};
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,207 @@
#pragma once
#include <stdx/detail/list_common.hpp>
#include <cstddef>
#if __cplusplus < 202002L
#include <iterator>
#endif
#include <type_traits>
namespace stdx {
inline namespace v1 {
template <STDX_DOUBLE_LINKABLE NodeType,
template <typename> typename P = node_policy::checked>
class intrusive_list {
friend P<NodeType>;
template <typename N> struct iterator_t {
using difference_type = std::ptrdiff_t;
using value_type = N;
using pointer = value_type *;
using reference = value_type &;
#if __cplusplus < 202002L
using iterator_category = std::forward_iterator_tag;
#endif
constexpr iterator_t() = default;
constexpr explicit iterator_t(pointer n) : node{n} {}
constexpr auto operator*() -> reference { return *node; }
constexpr auto operator*() const -> reference { return *node; }
constexpr auto operator->() -> pointer { return node; }
constexpr auto operator->() const -> pointer { return node; }
constexpr auto operator++() -> iterator_t & {
node = node->next;
return *this;
}
constexpr auto operator++(int) -> iterator_t {
auto tmp = *this;
++(*this);
return tmp;
}
private:
pointer node{};
#if __cpp_impl_three_way_comparison < 201907L
friend constexpr auto operator==(iterator_t lhs,
iterator_t rhs) -> bool {
return lhs.node == rhs.node;
}
friend constexpr auto operator!=(iterator_t lhs,
iterator_t rhs) -> bool {
return not(lhs == rhs);
}
#else
friend constexpr auto operator==(iterator_t,
iterator_t) -> bool = default;
#endif
};
public:
using value_type = NodeType;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type &;
using const_reference = value_type const &;
using pointer = value_type *;
using const_pointer = value_type const *;
using iterator = iterator_t<value_type>;
using const_iterator = iterator_t<value_type const>;
private:
pointer head{};
pointer tail{};
constexpr auto unchecked_push_front(pointer n) -> void {
if (head != nullptr) {
head->prev = n;
}
n->next = head;
head = n;
n->prev = nullptr;
if (tail == nullptr) {
tail = n;
}
}
constexpr auto unchecked_push_back(pointer n) -> void {
if (tail != nullptr) {
tail->next = n;
}
n->prev = tail;
tail = n;
n->next = nullptr;
if (head == nullptr) {
head = n;
}
}
constexpr auto unchecked_insert(iterator it, pointer n) -> void {
if (it != end()) {
auto p = it.operator->();
n->next = p;
n->prev = p->prev;
p->prev = n;
} else {
n->prev = tail;
tail = n;
}
if (n->prev) {
n->prev->next = n;
} else {
head = n;
}
}
public:
constexpr auto begin() -> iterator { return iterator{head}; }
constexpr auto begin() const -> const_iterator {
return const_iterator{head};
}
constexpr auto cbegin() const -> const_iterator {
return const_iterator{head};
}
constexpr auto end() -> iterator { return {}; }
constexpr auto end() const -> const_iterator { return {}; }
constexpr auto cend() const -> const_iterator { return {}; }
constexpr auto front() const -> reference { return *head; }
constexpr auto back() const -> reference { return *tail; }
constexpr auto push_front(pointer n) -> void {
P<NodeType>::push_front(*this, n);
}
constexpr auto push_back(pointer n) -> void {
P<NodeType>::push_back(*this, n);
}
constexpr auto insert(iterator it, pointer n) -> void {
if (empty()) {
push_back(n);
} else {
P<NodeType>::insert(*this, it, n);
}
}
constexpr auto pop_front() -> pointer {
pointer poppedNode = head;
head = head->next;
if (head == nullptr) {
tail = nullptr;
} else {
head->prev = nullptr;
}
P<NodeType>::on_pop(poppedNode);
return poppedNode;
}
constexpr auto pop_back() -> pointer {
pointer poppedNode = tail;
tail = tail->prev;
if (tail == nullptr) {
head = nullptr;
} else {
tail->next = nullptr;
}
P<NodeType>::on_pop(poppedNode);
return poppedNode;
}
[[nodiscard]] constexpr auto empty() const -> bool {
return head == nullptr;
}
constexpr auto clear() -> void {
P<NodeType>::on_clear(head);
head = nullptr;
tail = nullptr;
}
constexpr auto remove(pointer n) -> void {
pointer nextNode = n->next;
pointer prevNode = n->prev;
if (prevNode == nullptr) {
head = nextNode;
} else {
prevNode->next = nextNode;
}
if (nextNode == nullptr) {
tail = prevNode;
} else {
nextNode->prev = prevNode;
}
P<NodeType>::on_pop(n);
}
};
#undef STDX_DOUBLE_LINKABLE
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,47 @@
#pragma once
#include <stdx/type_traits.hpp>
#include <array>
#include <cstddef>
namespace stdx {
inline namespace v1 {
namespace detail {
template <typename T> struct ct_capacity_fail {
static_assert(always_false_v<stdx::remove_cvref_t<T>>,
"Type does not support compile-time capacity");
};
} // namespace detail
template <typename T>
constexpr auto ct_capacity_v = detail::ct_capacity_fail<T>{};
template <typename T, std::size_t N>
constexpr auto ct_capacity_v<std::array<T, N>> = N;
template <typename T> constexpr auto ct_capacity_v<T const> = ct_capacity_v<T>;
template <typename T> constexpr auto ct_capacity(T &&) -> std::size_t {
return ct_capacity_v<remove_cvref_t<T>>;
}
} // namespace v1
} // namespace stdx
#if __has_include(<span>)
#include <span>
#if __cpp_lib_span >= 202002L
namespace stdx {
inline namespace v1 {
template <typename T, std::size_t N>
constexpr auto ct_capacity_v<std::span<T, N>> = N;
template <typename T>
constexpr auto ct_capacity_v<std::span<T, std::dynamic_extent>> =
detail::ct_capacity_fail<std::span<T, std::dynamic_extent>>{};
} // namespace v1
} // namespace stdx
#endif
#endif

View File

@@ -0,0 +1,33 @@
#pragma once
#include <stdx/type_traits.hpp>
#include <memory>
namespace stdx {
inline namespace v1 {
template <typename T> constexpr auto to_address(T *p) noexcept -> T * {
static_assert(not stdx::is_function_v<T>,
"to_address on a function pointer is ill-formed");
return p;
}
namespace detail::detect {
template <typename T, typename = void>
constexpr auto pointer_traits_to_address = false;
template <typename T>
constexpr auto pointer_traits_to_address<
T, std::void_t<decltype(std::pointer_traits<T>::to_address(
std::declval<T>()))>> = true;
} // namespace detail::detect
template <typename T> constexpr auto to_address(T const &t) {
if constexpr (detail::detect::pointer_traits_to_address<T>) {
return std::pointer_traits<T>::to_address(t);
} else {
return stdx::to_address(t.operator->());
}
}
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,71 @@
#pragma once
#include <algorithm>
#include <functional>
#include <limits>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
#if __cplusplus >= 202002L
#define CONSTEXPR_INVOKE constexpr
#else
#define CONSTEXPR_INVOKE
#endif
template <typename T, typename InputIt, typename ROp, typename TOp,
typename... InputItN>
CONSTEXPR_INVOKE auto transform_reduce(InputIt first, InputIt last, T init,
ROp rop, TOp top,
InputItN... first_n) -> T {
while (first != last) {
init = std::invoke(rop, std::move(init),
std::invoke(top, *first, *first_n...));
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
}
return init;
}
template <typename T, typename InputIt, typename Size, typename ROp,
typename TOp, typename... InputItN>
CONSTEXPR_INVOKE auto transform_reduce_n(InputIt first, Size n, T init, ROp rop,
TOp top, InputItN... first_n) -> T {
while (n-- != 0) {
init = std::invoke(rop, std::move(init),
std::invoke(top, *first, *first_n...));
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
}
return init;
}
#undef CONSTEXPR_INVOKE
template <typename To, typename From>
constexpr auto saturate_cast(From from) -> To {
constexpr auto to_min = std::numeric_limits<To>::min();
constexpr auto to_max = std::numeric_limits<To>::max();
if constexpr (sizeof(From) > sizeof(To)) {
auto const clamped = std::clamp<From>(from, to_min, to_max);
return static_cast<To>(clamped);
}
if constexpr (sizeof(From) == sizeof(To)) {
if constexpr (std::is_unsigned_v<From> and std::is_signed_v<To>) {
if (from > to_max) {
return to_max;
}
}
if constexpr (std::is_signed_v<From> and std::is_unsigned_v<To>) {
if (from < 0) {
return static_cast<To>(0);
}
}
}
return static_cast<To>(from);
}
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,262 @@
#pragma once
#include <stdx/functional.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/utility.hpp>
#include <limits>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
// NOLINTBEGIN(modernize-use-constraints)
namespace stdx {
inline namespace v1 {
template <typename T, typename = void> struct tombstone_traits {
static_assert(
stdx::always_false_v<T>,
"To use stdx::optional you must specialize stdx::tombstone_traits");
};
template <typename T>
struct tombstone_traits<T, std::enable_if_t<std::is_floating_point_v<T>>> {
constexpr auto operator()() const {
return std::numeric_limits<T>::infinity();
}
};
template <typename T>
struct tombstone_traits<T, std::enable_if_t<std::is_pointer_v<T>>> {
constexpr auto operator()() const { return nullptr; }
};
template <auto V> struct tombstone_value {
constexpr auto operator()() const {
if constexpr (stdx::is_cx_value_v<decltype(V)>) {
return V();
} else {
return V;
}
}
};
template <typename T, typename TS = tombstone_traits<T>> class optional {
static_assert(not std::is_integral_v<T> or
not stdx::is_specialization_of_v<TS, tombstone_traits>,
"Don't define tombstone traits for plain integral types");
constexpr static inline auto traits = TS{};
T val{traits()};
public:
using value_type = T;
constexpr optional() = default;
constexpr explicit optional(std::nullopt_t) {}
template <typename... Args>
constexpr explicit optional(std::in_place_t, Args &&...args)
: val{std::forward<Args>(args)...} {}
template <
typename U = T,
typename = std::enable_if_t<
std::is_constructible_v<T, U &&> and
not std::is_same_v<stdx::remove_cvref_t<U>, std::in_place_t> and
not std::is_same_v<stdx::remove_cvref_t<U>, optional>>>
constexpr explicit optional(U &&u) : val{std::forward<U>(u)} {}
constexpr auto operator=(std::nullopt_t) -> optional & {
reset();
return *this;
}
template <
typename U = T,
typename = std::enable_if_t<
std::is_constructible_v<T, U> and std::is_assignable_v<T &, U> and
not std::is_same_v<stdx::remove_cvref_t<U>, optional> and
(std::is_scalar_v<T> or not std::is_same_v<std::decay_t<U>, T>)>>
constexpr auto operator=(U &&u) -> optional & {
val = std::forward<U>(u);
return *this;
}
[[nodiscard]] constexpr auto has_value() const noexcept -> bool {
return not(val == traits());
}
constexpr explicit operator bool() const noexcept { return has_value(); }
[[nodiscard]] constexpr auto value() & LIFETIMEBOUND -> value_type & {
return val;
}
[[nodiscard]] constexpr auto value() const
& LIFETIMEBOUND -> value_type const & {
return val;
}
[[nodiscard]] constexpr auto value() && LIFETIMEBOUND -> value_type && {
return std::move(val);
}
[[nodiscard]] constexpr auto value() const
&& LIFETIMEBOUND -> value_type const && {
return std::move(val);
}
[[nodiscard]] constexpr auto
operator->() const LIFETIMEBOUND->value_type const * {
return std::addressof(val);
}
[[nodiscard]] constexpr auto operator->() LIFETIMEBOUND->value_type * {
return std::addressof(val);
}
[[nodiscard]] constexpr auto operator*() const
& LIFETIMEBOUND->decltype(auto) {
return value();
}
[[nodiscard]] constexpr auto operator*() & LIFETIMEBOUND->decltype(auto) {
return value();
}
[[nodiscard]] constexpr auto operator*() const
&& LIFETIMEBOUND->decltype(auto) {
return std::move(*this).value();
}
[[nodiscard]] constexpr auto operator*() && LIFETIMEBOUND->decltype(auto) {
return std::move(*this).value();
}
template <typename U>
[[nodiscard]] constexpr auto
value_or(U &&default_value) const & -> value_type {
return has_value() ? val : T{std::forward<U>(default_value)};
}
template <typename U>
[[nodiscard]] constexpr auto value_or(U &&default_value) && -> value_type {
return has_value() ? std::move(val) : T{std::forward<U>(default_value)};
}
template <typename... Args>
constexpr auto emplace(Args &&...args) LIFETIMEBOUND -> value_type & {
val.~value_type();
new (std::addressof(val)) value_type(std::forward<Args>(args)...);
return value();
}
constexpr auto reset() {
val.~value_type();
new (std::addressof(val)) value_type(traits());
}
template <typename F> constexpr auto transform(F &&f) & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(val); }}}
: optional<U>{};
}
template <typename F> constexpr auto transform(F &&f) const & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type const &>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(val); }}}
: optional<U>{};
}
template <typename F> constexpr auto transform(F &&f) && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &&>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(std::move(val)); }}}
: optional<U>{};
}
template <typename F> constexpr auto transform(F &&f) const && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type const &&>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(std::move(val)); }}}
: optional<U>{};
}
template <typename F> constexpr auto or_else(F &&f) const & -> optional {
return *this ? *this : std::forward<F>(f)();
}
template <typename F> constexpr auto or_else(F &&f) && -> optional {
return *this ? std::move(*this) : std::forward<F>(f)();
}
template <typename F> constexpr auto and_then(F &&f) & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &>;
return *this ? std::forward<F>(f)(val) : U{};
}
template <typename F> constexpr auto and_then(F &&f) const & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type const &>;
return *this ? std::forward<F>(f)(val) : U{};
}
template <typename F> constexpr auto and_then(F &&f) && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &&>;
return *this ? std::forward<F>(f)(std::move(val)) : U{};
}
template <typename F> constexpr auto and_then(F &&f) const && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &&>;
return *this ? std::forward<F>(f)(std::move(val)) : U{};
}
private:
[[nodiscard]] friend constexpr auto
operator==(optional const &lhs, optional const &rhs) -> bool {
return lhs.val == rhs.val;
}
#if __cpp_impl_three_way_comparison < 201907L
[[nodiscard]] friend constexpr auto
operator!=(optional const &lhs, optional const &rhs) -> bool {
return not(lhs == rhs);
}
#endif
[[nodiscard]] friend constexpr auto operator<(optional const &lhs,
optional const &rhs) -> bool {
return lhs.has_value() and rhs.has_value()
? lhs.val < rhs.val
: not lhs.has_value() and rhs.has_value();
}
[[nodiscard]] friend constexpr auto
operator<=(optional const &lhs, optional const &rhs) -> bool {
return not(rhs < lhs);
}
[[nodiscard]] friend constexpr auto operator>(optional const &lhs,
optional const &rhs) -> bool {
return rhs < lhs;
}
[[nodiscard]] friend constexpr auto
operator>=(optional const &lhs, optional const &rhs) -> bool {
return not(lhs < rhs);
}
};
template <typename T> optional(T) -> optional<T>;
template <typename F, typename... Ts,
typename = std::enable_if_t<
(... and stdx::is_specialization_of_v<stdx::remove_cvref_t<Ts>,
optional>)>>
constexpr auto transform(F &&f, Ts &&...ts) {
using func_t = stdx::remove_cvref_t<F>;
using R = std::invoke_result_t<
func_t,
forward_like_t<Ts, typename stdx::remove_cvref_t<Ts>::value_type>...>;
if ((... and ts.has_value())) {
return optional<R>{with_result_of{[&] {
return std::forward<F>(f)(std::forward<Ts>(ts).value()...);
}}};
}
return optional<R>{};
}
} // namespace v1
} // namespace stdx
// NOLINTEND(modernize-use-constraints)

View File

@@ -0,0 +1,46 @@
#pragma once
#include <stdx/ct_string.hpp>
#include <utility>
namespace stdx {
inline namespace v1 {
struct default_panic_handler {
template <typename... Args>
static auto panic(Args &&...) noexcept -> void {}
#if __cplusplus >= 202002L
template <ct_string, typename... Args>
static auto panic(Args &&...) noexcept -> void {}
#endif
};
template <typename...> inline auto panic_handler = default_panic_handler{};
template <typename... Ts, typename... Args> auto panic(Args &&...args) -> void {
panic_handler<Ts...>.panic(std::forward<Args>(args)...);
}
#if __cplusplus >= 202002L
template <ct_string S, typename... Ts, typename... Args>
auto panic(Args &&...args) -> void {
panic_handler<Ts...>.template panic<S>(std::forward<Args>(args)...);
}
#endif
} // namespace v1
} // namespace stdx
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if __cplusplus >= 202002L
#define STDX_PANIC(MSG, ...) \
[] { \
using stdx::ct_string_literals::operator""_cts; \
stdx::panic<MSG##_cts>(__VA_ARGS__); \
}()
#else
#define STDX_PANIC(...) stdx::panic(__VA_ARGS__)
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)

View File

@@ -0,0 +1,12 @@
#pragma once
#include <cstddef>
namespace stdx {
inline namespace v1 {
template <std::size_t N> struct priority_t : priority_t<N - 1> {};
template <> struct priority_t<0> {};
template <std::size_t N> constexpr inline auto priority = priority_t<N>{};
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,39 @@
#pragma once
#include <iterator>
namespace stdx {
inline namespace v1 {
#if __cplusplus < 202002L
// NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
#define DETECTOR(name, expr) \
namespace detail::detect { \
template <typename T, typename = void> constexpr auto name = false; \
template <typename T> \
constexpr auto name<T, std::void_t<decltype(expr)>> = true; \
}
// NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
DETECTOR(range_begin, (std::begin(std::declval<T &>())))
DETECTOR(range_end, (std::end(std::declval<T &>())))
template <typename T>
constexpr auto range =
detail::detect::range_begin<T> and detail::detect::range_end<T>;
#undef DETECTOR
#else
template <typename T>
concept range = requires(T &t) {
std::begin(t);
std::end(t);
};
#endif
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,131 @@
#pragma once
#include <stdx/concepts.hpp>
#include <type_traits>
namespace stdx {
inline namespace v1 {
template <typename T> struct rollover_t {
static_assert(unsigned_integral<T>,
"Argument to rollover_t must be an unsigned integral type.");
using underlying_t = T;
constexpr rollover_t() = default;
template <typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
constexpr explicit rollover_t(U u) : value{static_cast<underlying_t>(u)} {}
template <typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
constexpr explicit rollover_t(rollover_t<U> u)
: rollover_t{static_cast<U>(u)} {}
[[nodiscard]] constexpr auto as_underlying() const -> underlying_t {
return value;
}
constexpr explicit operator underlying_t() const { return value; }
[[nodiscard]] constexpr auto operator+() const -> rollover_t {
return *this;
}
[[nodiscard]] constexpr auto operator-() const -> rollover_t {
return rollover_t{static_cast<underlying_t>(-value)};
}
constexpr auto operator++() -> rollover_t & {
++value;
return *this;
}
constexpr auto operator++(int) -> rollover_t { return rollover_t{value++}; }
constexpr auto operator--() -> rollover_t & {
--value;
return *this;
}
constexpr auto operator--(int) -> rollover_t { return rollover_t{value--}; }
constexpr auto operator+=(rollover_t other) -> rollover_t & {
value += other.value;
return *this;
}
constexpr auto operator-=(rollover_t other) -> rollover_t & {
value -= other.value;
return *this;
}
constexpr auto operator*=(rollover_t other) -> rollover_t & {
value *= other.value;
return *this;
}
constexpr auto operator/=(rollover_t other) -> rollover_t & {
value /= other.value;
return *this;
}
constexpr auto operator%=(rollover_t other) -> rollover_t & {
value %= other.value;
return *this;
}
private:
[[nodiscard]] constexpr friend auto operator==(rollover_t lhs,
rollover_t rhs) -> bool {
return lhs.value == rhs.value;
}
[[nodiscard]] constexpr friend auto operator!=(rollover_t lhs,
rollover_t rhs) -> bool {
return not(lhs == rhs);
}
constexpr friend auto operator<(rollover_t, rollover_t) -> bool = delete;
constexpr friend auto operator<=(rollover_t, rollover_t) -> bool = delete;
constexpr friend auto operator>(rollover_t, rollover_t) -> bool = delete;
constexpr friend auto operator>=(rollover_t, rollover_t) -> bool = delete;
[[nodiscard]] constexpr friend auto cmp_less(rollover_t lhs,
rollover_t rhs) -> bool {
constexpr auto mid = static_cast<underlying_t>(~underlying_t{}) / 2;
return static_cast<underlying_t>(lhs.value - rhs.value) > mid;
}
[[nodiscard]] constexpr friend auto
operator+(rollover_t lhs, rollover_t rhs) -> rollover_t {
lhs += rhs;
return lhs;
}
[[nodiscard]] constexpr friend auto
operator-(rollover_t lhs, rollover_t rhs) -> rollover_t {
lhs -= rhs;
return lhs;
}
[[nodiscard]] constexpr friend auto
operator*(rollover_t lhs, rollover_t rhs) -> rollover_t {
lhs *= rhs;
return lhs;
}
[[nodiscard]] constexpr friend auto
operator/(rollover_t lhs, rollover_t rhs) -> rollover_t {
lhs /= rhs;
return lhs;
}
[[nodiscard]] constexpr friend auto
operator%(rollover_t lhs, rollover_t rhs) -> rollover_t {
lhs %= rhs;
return lhs;
}
underlying_t value{};
};
template <typename T> rollover_t(T) -> rollover_t<T>;
} // namespace v1
} // namespace stdx
template <typename T, typename U>
struct std::common_type<stdx::rollover_t<T>, stdx::rollover_t<U>> {
using type = stdx::rollover_t<std::common_type_t<T, U>>;
};
template <typename T, typename I>
struct std::common_type<stdx::rollover_t<T>, I> {
using type =
stdx::rollover_t<std::common_type_t<T, std::make_unsigned_t<I>>>;
};

View File

@@ -0,0 +1,286 @@
#pragma once
#include <stdx/bit.hpp>
#include <stdx/compiler.hpp>
#include <stdx/iterator.hpp>
#include <stdx/memory.hpp>
#include <stdx/type_traits.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <limits>
#include <type_traits>
// NOLINTBEGIN(modernize-use-constraints)
namespace stdx {
inline namespace v1 {
constexpr static auto dynamic_extent = std::numeric_limits<std::size_t>::max();
namespace detail {
template <typename T, std::size_t N> struct span_base {
constexpr span_base() = default;
template <typename It, typename SizeOrEnd>
constexpr explicit span_base(It, SizeOrEnd) {}
constexpr static std::integral_constant<std::size_t, N> size{};
constexpr static std::integral_constant<std::size_t, N * sizeof(T)>
size_bytes{};
constexpr static std::bool_constant<N == 0> empty{};
};
template <typename T> class span_base<T, dynamic_extent> {
std::size_t sz{};
public:
constexpr span_base() = default;
template <typename It, typename SizeOrEnd,
std::enable_if_t<std::is_integral_v<SizeOrEnd>, int> = 0>
constexpr span_base(It, SizeOrEnd count)
: sz{static_cast<std::size_t>(count)} {}
template <typename It, typename SizeOrEnd,
std::enable_if_t<not std::is_integral_v<SizeOrEnd>, int> = 0>
constexpr span_base(It first, SizeOrEnd last)
: sz{static_cast<std::size_t>(std::distance(first, last))} {}
[[nodiscard]] constexpr auto size() const noexcept -> std::size_t {
return sz;
}
[[nodiscard]] constexpr auto size_bytes() const noexcept -> std::size_t {
return sz * sizeof(T);
}
[[nodiscard]] constexpr auto empty() const noexcept -> bool {
return sz == 0u;
}
};
} // namespace detail
template <typename T, std::size_t Extent = dynamic_extent>
class span : public detail::span_base<T, Extent> {
template <typename> constexpr static inline auto dependent_extent = Extent;
using base_t = detail::span_base<T, Extent>;
T *ptr{};
public:
using element_type = T;
using value_type = stdx::remove_cvref_t<T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using const_pointer = T const *;
using reference = T &;
using const_reference = T const &;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr static inline auto extent = Extent;
constexpr span() = default;
template <typename It, typename SizeOrEnd,
std::enable_if_t<dependent_extent<It> != dynamic_extent, int> = 0>
explicit constexpr span(It first, SizeOrEnd)
: ptr{stdx::to_address(first)} {}
template <typename It, typename SizeOrEnd,
std::enable_if_t<dependent_extent<It> == dynamic_extent, int> = 0>
constexpr span(It first, SizeOrEnd sore)
: base_t{first, sore}, ptr{stdx::to_address(first)} {}
template <typename U, std::size_t N>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr span(std::array<U, N> &arr LIFETIMEBOUND) noexcept
: ptr{std::data(arr)} {
static_assert(Extent == dynamic_extent or Extent <= N,
"Span extends beyond available storage");
}
template <typename U, std::size_t N>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr span(std::array<U, N> const &arr LIFETIMEBOUND) noexcept
: ptr{std::data(arr)} {
static_assert(Extent == dynamic_extent or Extent <= N,
"Span extends beyond available storage");
}
template <std::size_t N>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr span(
// NOLINTNEXTLINE(*-avoid-c-arrays)
stdx::type_identity_t<element_type> (&arr)[N] LIFETIMEBOUND) noexcept
: ptr{std::data(arr)} {
static_assert(Extent == dynamic_extent or Extent <= N,
"Span extends beyond available storage");
}
template <typename R,
std::enable_if_t<dependent_extent<R> != dynamic_extent, int> = 0>
explicit constexpr span(R &&r)
: ptr{stdx::to_address(std::begin(std::forward<R>(r)))} {}
template <typename R,
std::enable_if_t<dependent_extent<R> == dynamic_extent, int> = 0>
explicit constexpr span(R &&r)
: base_t{std::begin(std::forward<R>(r)), std::end(std::forward<R>(r))},
ptr{stdx::to_address(std::begin(std::forward<R>(r)))} {}
template <class U, std::size_t N,
std::enable_if_t<dependent_extent<U> != dynamic_extent and
N == dynamic_extent,
int> = 0>
explicit constexpr span(span<U, N> const &s) noexcept : ptr{s.data()} {}
template <class U, std::size_t N,
std::enable_if_t<dependent_extent<U> == dynamic_extent or
N != dynamic_extent,
int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr span(span<U, N> const &s) noexcept
: base_t{s.data(), s.size()}, ptr{s.data()} {}
[[nodiscard]] constexpr auto data() const noexcept -> pointer {
return ptr;
}
[[nodiscard]] constexpr auto begin() const noexcept -> iterator {
return ptr;
}
[[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator {
return ptr;
}
[[nodiscard]] constexpr auto rbegin() const noexcept -> reverse_iterator {
return std::reverse_iterator{end()};
}
[[nodiscard]] constexpr auto
crbegin() const noexcept -> const_reverse_iterator {
return std::reverse_iterator{cend()};
}
[[nodiscard]] constexpr auto end() const noexcept -> iterator {
return ptr + this->size();
}
[[nodiscard]] constexpr auto cend() const noexcept -> const_iterator {
return ptr + this->size();
}
[[nodiscard]] constexpr auto rend() const noexcept -> reverse_iterator {
return std::reverse_iterator{begin()};
}
[[nodiscard]] constexpr auto
crend() const noexcept -> const_reverse_iterator {
return std::reverse_iterator{cbegin()};
}
[[nodiscard]] constexpr auto front() const -> reference { return *begin(); }
[[nodiscard]] constexpr auto back() const -> reference {
return *std::prev(end());
}
[[nodiscard]] constexpr auto operator[](size_type idx) const -> reference {
return data()[idx];
}
template <std::size_t Count>
[[nodiscard]] constexpr auto first() const -> span<element_type, Count> {
static_assert(Count <= Extent, "first cannot form a larger span!");
return span<element_type, Count>{ptr, Count};
}
[[nodiscard]] constexpr auto
first(size_type count) const -> span<element_type, dynamic_extent> {
return {ptr, count};
}
template <std::size_t Count>
[[nodiscard]] constexpr auto last() const -> span<element_type, Count> {
static_assert(Count <= Extent, "last cannot form a larger span!");
return span<element_type, Count>{ptr + this->size() - Count, Count};
}
[[nodiscard]] constexpr auto
last(size_type count) const -> span<element_type, dynamic_extent> {
return {ptr + this->size() - count, count};
}
template <std::size_t Offset, std::size_t Count = dynamic_extent>
[[nodiscard]] constexpr auto subspan() const {
if constexpr (Count != dynamic_extent) {
static_assert(Offset <= Extent,
"subspan cannot start beyond span!");
static_assert(Count <= Extent,
"subspan cannot be longer than span!");
static_assert(Offset <= Extent - Count,
"subspan cannot end beyond span!");
return span<element_type, Count>{ptr + Offset, Count};
} else if constexpr (Extent != dynamic_extent) {
static_assert(Offset <= Extent,
"subspan cannot start beyond span!");
return span<element_type, Extent - Offset>{ptr + Offset,
Extent - Offset};
} else {
return span<element_type, dynamic_extent>{ptr + Offset,
this->size() - Offset};
}
}
[[nodiscard]] constexpr auto subspan(size_type Offset,
size_type Count = dynamic_extent) const
-> span<element_type, dynamic_extent> {
return {ptr + Offset, std::min(Count, this->size() - Offset)};
}
};
// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
template <class T, std::size_t N> auto as_bytes(span<T, N> s) noexcept {
if constexpr (N == dynamic_extent) {
return span{reinterpret_cast<std::byte const *>(s.data()),
s.size_bytes()};
} else {
constexpr auto size = s.size_bytes();
return span<std::byte const, size>{
reinterpret_cast<std::byte const *>(s.data()), size};
}
}
template <class T, std::size_t N,
std::enable_if_t<not std::is_const_v<T>, int> = 0>
auto as_writable_bytes(span<T, N> s) noexcept {
if constexpr (N == dynamic_extent) {
return span{reinterpret_cast<std::byte *>(s.data()), s.size_bytes()};
} else {
constexpr auto size = s.size_bytes();
return span<std::byte, size>{reinterpret_cast<std::byte *>(s.data()),
size};
}
}
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
namespace detail {
template <typename T> using iter_reference_t = decltype(*std::declval<T &>());
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T &>()));
template <typename R> using range_reference_t = iter_reference_t<iterator_t<R>>;
} // namespace detail
template <typename It, typename EndOrSize>
span(It,
EndOrSize) -> span<std::remove_reference_t<detail::iter_reference_t<It>>>;
// NOLINTNEXTLINE(*-avoid-c-arrays)
template <typename T, std::size_t N> span(T (&)[N]) -> span<T, N>;
template <typename T, std::size_t N> span(std::array<T, N> &) -> span<T, N>;
template <typename T, std::size_t N>
span(std::array<T, N> const &) -> span<T const, N>;
template <typename R>
span(R &&) -> span<std::remove_reference_t<detail::range_reference_t<R>>>;
template <typename T, std::size_t N>
constexpr auto ct_capacity_v<span<T, N>> = N;
} // namespace v1
} // namespace stdx
// NOLINTEND(modernize-use-constraints)

View File

@@ -0,0 +1,48 @@
#pragma once
#if __cplusplus >= 202002L
#include <stdx/ct_format.hpp>
#include <stdx/ct_string.hpp>
namespace stdx {
inline namespace v1 {
struct ct_check_value {};
template <bool B> struct ct_check_t {
template <ct_string S> constexpr static bool stаtiс_аssert = false;
template <ct_string S>
constexpr static auto emit() -> ct_check_value
requires stаtiс_аssert<S>;
};
template <> struct ct_check_t<true> {
template <ct_string S> constexpr static auto emit() -> ct_check_value {
return {};
}
};
template <bool B> constexpr auto ct_check = ct_check_t<B>{};
namespace detail {
template <ct_string Fmt, auto... Args> constexpr auto static_format() {
constexpr auto make_ct = []<auto V>() {
if constexpr (cx_value<decltype(V)>) {
return V;
} else {
return CX_VALUE(V);
}
};
return ct_format<Fmt>(make_ct.template operator()<Args>()...).str.value;
}
} // namespace detail
} // namespace v1
} // namespace stdx
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define STATIC_ASSERT(cond, ...) \
[]<bool B>() -> bool { \
stdx::ct_check<B>.template emit<stdx::detail::static_format<__VA_ARGS__>()>(); \
return B; \
}.template operator()<cond>()
#endif

View File

@@ -0,0 +1,509 @@
#pragma once
#if __cplusplus >= 202002L
#include <stdx/udls.hpp>
#include <array>
#include <concepts>
#include <cstddef>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <std::size_t I>
using index_constant = std::integral_constant<std::size_t, I>;
template <std::size_t I> constexpr static index_constant<I> index{};
inline namespace literals {
template <char... Chars> CONSTEVAL auto operator""_idx() {
return index<parse_literal<std::size_t, Chars...>()>;
}
} // namespace literals
template <typename> struct tag_constant;
template <typename T> constexpr static tag_constant<T> *tag{};
namespace error {
template <typename...> constexpr auto always_false_v = false;
template <typename T> struct type_from_tag_constant {
using type = T;
};
template <typename T> struct type_from_tag_constant<tag_constant<T> *> {
using type = T;
};
template <typename> struct looking_for;
template <typename...> struct in_tuple;
template <auto> struct index;
template <auto> struct max_index;
template <typename T, typename... Ts> constexpr auto type_not_found() {
using type = typename type_from_tag_constant<T>::type;
static_assert(always_false_v<looking_for<type>, in_tuple<Ts...>>,
"Type not found in tuple!");
}
template <auto I, typename... Ts> constexpr auto index_out_of_bounds() {
static_assert(
always_false_v<index<I>, max_index<sizeof...(Ts) - 1>, in_tuple<Ts...>>,
"Tuple index out of bounds!");
}
} // namespace error
namespace detail {
template <std::size_t Index, typename T, typename... Ts> struct element {
#if __has_builtin(__type_pack_element)
using type = T;
#else
constexpr static auto ugly_Value(index_constant<Index>) -> T;
[[nodiscard]] constexpr auto ugly_iGet_clvr(
index_constant<Index>) const & noexcept LIFETIMEBOUND -> T const & {
return value;
}
[[nodiscard]] constexpr auto
ugly_iGet_lvr(index_constant<Index>) & noexcept LIFETIMEBOUND -> T & {
return value;
}
[[nodiscard]] constexpr auto
ugly_iGet_rvr(index_constant<Index>) && noexcept LIFETIMEBOUND -> T && {
return std::forward<T>(value);
}
#endif
template <typename U>
requires(std::same_as<U, T> or ... or std::same_as<U, Ts>)
[[nodiscard]] constexpr auto ugly_tGet_clvr(
tag_constant<U> *) const & noexcept LIFETIMEBOUND -> T const & {
return value;
}
template <typename U>
requires(std::same_as<U, T> or ... or std::same_as<U, Ts>)
[[nodiscard]] constexpr auto
ugly_tGet_lvr(tag_constant<U> *) & noexcept LIFETIMEBOUND -> T & {
return value;
}
template <typename U>
requires(std::same_as<U, T> or ... or std::same_as<U, Ts>)
[[nodiscard]] constexpr auto
ugly_tGet_rvr(tag_constant<U> *) && noexcept LIFETIMEBOUND -> T && {
return std::forward<T>(value);
}
constexpr auto ugly_Value_clvr() const & LIFETIMEBOUND -> T const & {
return value;
}
constexpr auto ugly_Value_lvr() & LIFETIMEBOUND -> T & { return value; }
constexpr auto ugly_Value_rvr() && LIFETIMEBOUND -> T && {
return std::forward<T>(value);
}
T value;
private:
[[nodiscard]] friend constexpr auto
operator==(element const &, element const &) -> bool = default;
[[nodiscard]] friend constexpr auto operator<=>(element const &,
element const &) = default;
};
template <typename Op, typename Value> struct fold_helper {
Op op;
Value value;
private:
template <typename Rhs>
[[nodiscard]] friend constexpr auto operator+(fold_helper &&lhs,
Rhs &&rhs) {
using R =
decltype(lhs.op(std::move(lhs).value, std::forward<Rhs>(rhs)));
return fold_helper<Op, std::remove_cvref_t<R>>{
lhs.op, lhs.op(std::move(lhs).value, std::forward<Rhs>(rhs))};
}
template <typename Lhs>
[[nodiscard]] friend constexpr auto operator+(Lhs &&lhs,
fold_helper &&rhs) {
using R =
decltype(rhs.op(std::forward<Lhs>(lhs), std::move(rhs).value));
return fold_helper<Op, std::remove_cvref_t<R>>{
rhs.op, rhs.op(std::forward<Lhs>(lhs), std::move(rhs).value)};
}
};
template <typename Op, typename Value>
fold_helper(Op, Value) -> fold_helper<Op, std::remove_cvref_t<Value>>;
template <typename Op, typename Value> struct join_helper {
Op op;
Value value;
};
template <typename Op, typename Value>
join_helper(Op, Value) -> join_helper<Op, std::remove_cvref_t<Value>>;
// Note: operator+ is not a hidden friend of join_helper to avoid template
// instantiation abiguity
template <typename Op, typename T, typename U>
[[nodiscard]] constexpr auto operator+(join_helper<Op, T> &&lhs,
join_helper<Op, U> &&rhs) {
using R = decltype(lhs.op(std::move(lhs).value, std::move(rhs).value));
return join_helper<Op, std::remove_cvref_t<R>>{
lhs.op, lhs.op(std::move(lhs).value, std::move(rhs).value)};
}
template <template <typename> typename...> struct index_function_list;
template <typename...> struct tuple_impl;
template <template <typename> typename... Fs> struct element_helper {
template <std::size_t I, typename T>
using element_t = element<I, T, Fs<std::remove_cvref_t<T>>...>;
};
struct index_pair {
std::size_t outer;
std::size_t inner;
};
template <std::size_t... Is, template <typename> typename... Fs, typename... Ts>
struct tuple_impl<std::index_sequence<Is...>, index_function_list<Fs...>, Ts...>
: element_helper<Fs...>::template element_t<Is, Ts>... {
private:
template <std::size_t I, typename T>
using base_t = typename element_helper<Fs...>::template element_t<I, T>;
public:
using common_tuple_comparable = void;
using is_tuple = void;
using base_t<Is, Ts>::ugly_tGet_clvr...;
using base_t<Is, Ts>::ugly_tGet_lvr...;
using base_t<Is, Ts>::ugly_tGet_rvr...;
#if __has_builtin(__type_pack_element)
template <std::size_t I>
using element_t = typename base_t<I, __type_pack_element<I, Ts...>>::type;
#else
constexpr static auto ugly_Value(...) -> void;
using base_t<Is, Ts>::ugly_Value...;
template <std::size_t I> using element_t = decltype(ugly_Value(index<I>));
using base_t<Is, Ts>::ugly_iGet_clvr...;
using base_t<Is, Ts>::ugly_iGet_lvr...;
using base_t<Is, Ts>::ugly_iGet_rvr...;
#endif
template <typename Init, typename Op>
[[nodiscard]] constexpr inline auto fold_left(Init &&init,
Op &&op) const & {
return (fold_helper{op, std::forward<Init>(init)} + ... +
this->base_t<Is, Ts>::ugly_Value_clvr())
.value;
}
template <typename Init, typename Op>
[[nodiscard]] constexpr inline auto fold_left(Init &&init, Op &&op) && {
return (fold_helper{op, std::forward<Init>(init)} + ... +
std::move(*this).base_t<Is, Ts>::ugly_Value_rvr())
.value;
}
template <typename Init, typename Op>
[[nodiscard]] constexpr inline auto fold_right(Init &&init,
Op &&op) const & {
return (this->base_t<Is, Ts>::ugly_Value_clvr() + ... +
fold_helper{op, std::forward<Init>(init)})
.value;
}
template <typename Init, typename Op>
[[nodiscard]] constexpr inline auto fold_right(Init &&init, Op &&op) && {
return (std::move(*this).base_t<Is, Ts>::ugly_Value_rvr() + ... +
fold_helper{op, std::forward<Init>(init)})
.value;
}
template <std::size_t I>
[[nodiscard]] constexpr auto
operator[]([[maybe_unused]] index_constant<I> i) const
& LIFETIMEBOUND->decltype(auto) {
if constexpr (I >= sizeof...(Ts)) {
error::index_out_of_bounds<I, Ts...>();
} else {
#if __has_builtin(__type_pack_element)
using B = base_t<I, __type_pack_element<I, Ts...>>;
return this->B::ugly_Value_clvr();
#else
return this->ugly_iGet_clvr(i);
#endif
}
}
template <std::size_t I>
[[nodiscard]] constexpr auto
operator[]([[maybe_unused]] index_constant<I> i) &
LIFETIMEBOUND->decltype(auto) {
if constexpr (I >= sizeof...(Ts)) {
error::index_out_of_bounds<I, Ts...>();
} else {
#if __has_builtin(__type_pack_element)
using B = base_t<I, __type_pack_element<I, Ts...>>;
return this->B::ugly_Value_lvr();
#else
return this->ugly_iGet_lvr(i);
#endif
}
}
template <std::size_t I>
[[nodiscard]] constexpr auto
operator[]([[maybe_unused]] index_constant<I> i) &&
LIFETIMEBOUND->decltype(auto) {
if constexpr (I >= sizeof...(Ts)) {
error::index_out_of_bounds<I, Ts...>();
} else {
#if __has_builtin(__type_pack_element)
using B [[maybe_unused]] = base_t<I, __type_pack_element<I, Ts...>>;
return std::move(*this).B::ugly_Value_rvr();
#else
return std::move(*this).ugly_iGet_rvr(i);
#endif
}
}
constexpr auto ugly_tGet_clvr(auto idx) const & -> void {
error::type_not_found<decltype(idx), Ts...>();
}
constexpr auto ugly_tGet_lvr(auto idx) & -> void {
error::type_not_found<decltype(idx), Ts...>();
}
constexpr auto ugly_tGet_rvr(auto idx) && -> void {
error::type_not_found<decltype(idx), Ts...>();
}
[[nodiscard]] constexpr auto get(auto idx) const & -> decltype(auto) {
return this->ugly_tGet_clvr(idx);
}
[[nodiscard]] constexpr auto get(auto idx) & -> decltype(auto) {
return this->ugly_tGet_lvr(idx);
}
[[nodiscard]] constexpr auto get(auto idx) && -> decltype(auto) {
return std::move(*this).ugly_tGet_rvr(idx);
}
template <typename Op>
constexpr auto apply(Op &&op) const & -> decltype(auto) {
return std::forward<Op>(op)(this->base_t<Is, Ts>::ugly_Value_clvr()...);
}
template <typename Op> constexpr auto apply(Op &&op) & -> decltype(auto) {
return std::forward<Op>(op)(this->base_t<Is, Ts>::ugly_Value_lvr()...);
}
template <typename Op> constexpr auto apply(Op &&op) && -> decltype(auto) {
return std::forward<Op>(op)(
std::move(*this).base_t<Is, Ts>::ugly_Value_rvr()...);
}
template <typename Op>
requires(sizeof...(Ts) > 0)
constexpr auto join(Op &&op) const & -> decltype(auto) {
return (... + join_helper{op, this->base_t<Is, Ts>::ugly_Value_clvr()})
.value;
}
template <typename Op>
requires(sizeof...(Ts) > 0)
constexpr auto join(Op &&op) && -> decltype(auto) {
return (... +
join_helper{op,
std::move(*this).base_t<Is, Ts>::ugly_Value_rvr()})
.value;
}
template <typename Init, typename Op>
constexpr auto join(Init &&init, Op &&op) const & {
if constexpr (sizeof...(Ts) == 0) {
return init;
} else {
return this->join(std::forward<Op>(op));
}
}
template <typename Init, typename Op>
constexpr auto join(Init &&init, Op &&op) && {
if constexpr (sizeof...(Ts) == 0) {
return init;
} else {
return std::move(*this).join(std::forward<Op>(op));
}
}
constexpr static auto size =
std::integral_constant<std::size_t, sizeof...(Ts)>{};
[[nodiscard]] constexpr static auto
fill_inner_indices(index_pair *p) -> index_pair * {
((p++->inner = Is), ...);
return p;
}
[[nodiscard]] constexpr static auto
fill_outer_indices(index_pair *p,
[[maybe_unused]] std::size_t n) -> index_pair * {
((p++->outer = (static_cast<void>(Is), n)), ...);
return p;
}
private:
template <typename Funcs, typename... Us>
requires(... and std::equality_comparable_with<Ts, Us>)
[[nodiscard]] friend constexpr auto
operator==(tuple_impl const &lhs,
tuple_impl<std::index_sequence<Is...>, Funcs, Us...> const &rhs)
-> bool {
return (... and (lhs[index<Is>] == rhs[index<Is>]));
}
template <typename Funcs, typename... Us>
requires(... and std::three_way_comparable_with<Ts, Us>)
[[nodiscard]] friend constexpr auto operator<=>(
tuple_impl const &lhs,
tuple_impl<std::index_sequence<Is...>, Funcs, Us...> const &rhs) {
if constexpr (sizeof...(Is) == 0) {
return std::strong_ordering::equal;
} else {
using C =
std::common_comparison_category_t<decltype(lhs[index<Is>] <=>
rhs[index<Is>])...>;
C result = lhs[index<0>] <=> rhs[index<0>];
auto const compare_at = [&]<std::size_t I>() {
result = lhs[index<I>] <=> rhs[index<I>];
return result != 0;
};
[[maybe_unused]] auto b =
(compare_at.template operator()<Is>() or ...);
return result;
}
}
};
template <typename... Ts>
tuple_impl(Ts...)
-> tuple_impl<std::index_sequence_for<Ts...>, index_function_list<>, Ts...>;
} // namespace detail
template <typename T> constexpr auto tuple_size_v = T::size();
template <typename T, std::size_t N>
constexpr auto tuple_size_v<std::array<T, N>> = N;
template <std::size_t I, typename T>
using tuple_element_t = typename T::template element_t<I>;
template <typename T>
concept tuple_comparable = requires { typename T::common_tuple_comparable; };
template <typename T>
concept tuplelike = requires { typename remove_cvref_t<T>::is_tuple; };
template <typename... Ts>
class tuple : public detail::tuple_impl<std::index_sequence_for<Ts...>,
detail::index_function_list<>, Ts...> {
template <typename U>
requires(not tuple_comparable<U>)
[[nodiscard]] friend constexpr auto operator==(tuple const &,
U const &) -> bool = delete;
template <typename U>
requires(not tuple_comparable<U>)
[[nodiscard]] friend constexpr auto operator<=>(tuple const &,
U const &) = delete;
};
template <typename... Ts> tuple(Ts...) -> tuple<Ts...>;
template <typename IndexList, typename... Ts>
class indexed_tuple : public detail::tuple_impl<std::index_sequence_for<Ts...>,
IndexList, Ts...> {
template <typename U>
requires(not tuple_comparable<U>)
[[nodiscard]] friend constexpr auto operator==(indexed_tuple const &,
U const &) -> bool = delete;
template <typename U>
requires(not tuple_comparable<U>)
[[nodiscard]] friend constexpr auto operator<=>(indexed_tuple const &,
U const &) = delete;
};
template <typename... Ts>
indexed_tuple(Ts...) -> indexed_tuple<detail::index_function_list<>, Ts...>;
template <std::size_t I, tuplelike Tuple>
[[nodiscard]] constexpr auto
get(Tuple &&t LIFETIMEBOUND) -> decltype(std::forward<Tuple>(t)[index<I>]) {
return std::forward<Tuple>(t)[index<I>];
}
template <typename T, tuplelike Tuple>
[[nodiscard]] constexpr auto
get(Tuple &&t LIFETIMEBOUND) -> decltype(std::forward<Tuple>(t).get(tag<T>)) {
return std::forward<Tuple>(t).get(tag<T>);
}
template <typename... Ts> [[nodiscard]] constexpr auto make_tuple(Ts &&...ts) {
return tuple<std::remove_cvref_t<Ts>...>{std::forward<Ts>(ts)...};
}
template <template <typename> typename... Fs>
constexpr auto make_indexed_tuple = []<typename... Ts>(Ts &&...ts) {
return indexed_tuple<detail::index_function_list<Fs...>,
std::remove_cvref_t<Ts>...>{std::forward<Ts>(ts)...};
};
template <template <typename> typename... Fs, tuplelike T>
constexpr auto apply_indices(T &&t) {
using tuple_t = std::remove_cvref_t<T>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return indexed_tuple<detail::index_function_list<Fs...>,
tuple_element_t<Is, tuple_t>...>{
std::forward<T>(t)[index<Is>]...};
}(std::make_index_sequence<tuple_size_v<tuple_t>>{});
}
template <typename... Ts> constexpr auto forward_as_tuple(Ts &&...ts) {
return stdx::tuple<Ts &&...>{std::forward<Ts>(ts)...};
}
template <typename Op, tuplelike T>
constexpr auto apply(Op &&op, T &&t) -> decltype(auto) {
return std::forward<T>(t).apply(std::forward<Op>(op));
}
template <template <typename> typename... Fs, typename Op, tuplelike T>
constexpr auto transform(Op &&op, T &&t) {
if constexpr (sizeof...(Fs) == 0) {
return std::forward<T>(t).apply([&]<typename... Ts>(Ts &&...ts) {
return stdx::tuple<decltype(op(std::forward<Ts>(ts)))...>{
op(std::forward<Ts>(ts))...};
});
} else {
return std::forward<T>(t).apply([&]<typename... Ts>(Ts &&...ts) {
return stdx::make_indexed_tuple<Fs...>(op(std::forward<Ts>(ts))...);
});
}
}
template <typename Op, tuplelike T>
constexpr auto for_each(Op &&op, T &&t) -> Op {
return std::forward<T>(t).apply([&]<typename... Ts>(Ts &&...ts) {
(op(std::forward<Ts>(ts)), ...);
return op;
});
}
template <typename... Ts>
class one_of : public detail::tuple_impl<std::index_sequence_for<Ts...>,
detail::index_function_list<>, Ts...> {
template <typename T>
constexpr friend auto operator==(one_of const &lhs, T const &rhs) -> bool {
return lhs.apply(
[&](auto &&...args) { return ((args == rhs) || ...); });
}
};
template <typename... Ts> one_of(Ts...) -> one_of<Ts...>;
} // namespace v1
} // namespace stdx
#endif

View File

@@ -0,0 +1,447 @@
#pragma once
#if __cplusplus >= 202002L
#include <stdx/ct_conversions.hpp>
#include <stdx/tuple.hpp>
#include <stdx/type_traits.hpp>
#include <boost/mp11/algorithm.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <typename F, tuplelike... Ts> constexpr auto apply(F &&f, Ts &&...ts) {
constexpr auto total_num_elements =
(std::size_t{} + ... + stdx::tuple_size_v<std::remove_cvref_t<Ts>>);
[[maybe_unused]] constexpr auto element_indices = [&] {
std::array<detail::index_pair, total_num_elements> indices{};
[[maybe_unused]] auto p = indices.data();
((p = std::remove_cvref_t<Ts>::fill_inner_indices(p)), ...);
[[maybe_unused]] auto q = indices.data();
[[maybe_unused]] std::size_t n{};
((q = std::remove_cvref_t<Ts>::fill_outer_indices(q, n++)), ...);
return indices;
}();
[[maybe_unused]] auto outer_tuple =
stdx::tuple<Ts &&...>{std::forward<Ts>(ts)...};
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::forward<F>(f)(
std::move(outer_tuple)[index<element_indices[Is].outer>]
[index<element_indices[Is].inner>]...);
}(std::make_index_sequence<total_num_elements>{});
}
template <tuplelike... Ts> [[nodiscard]] constexpr auto tuple_cat(Ts &&...ts) {
if constexpr (sizeof...(Ts) == 0) {
return stdx::tuple<>{};
} else if constexpr (sizeof...(Ts) == 1) {
return (ts, ...);
} else {
constexpr auto total_num_elements =
(std::size_t{} + ... + stdx::tuple_size_v<std::remove_cvref_t<Ts>>);
[[maybe_unused]] constexpr auto element_indices = [&] {
std::array<detail::index_pair, total_num_elements> indices{};
auto p = indices.data();
((p = std::remove_cvref_t<Ts>::fill_inner_indices(p)), ...);
auto q = indices.data();
std::size_t n{};
((q = std::remove_cvref_t<Ts>::fill_outer_indices(q, n++)), ...);
return indices;
}();
[[maybe_unused]] auto outer_tuple =
stdx::tuple<Ts &&...>{std::forward<Ts>(ts)...};
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
using T = stdx::tuple<stdx::tuple_element_t<
element_indices[Is].inner,
std::remove_cvref_t<decltype(std::move(
outer_tuple)[index<element_indices[Is].outer>])>>...>;
return T{
std::move(outer_tuple)[index<element_indices[Is].outer>]
[index<element_indices[Is].inner>]...};
}(std::make_index_sequence<total_num_elements>{});
}
}
template <typename T, tuplelike Tup>
[[nodiscard]] constexpr auto tuple_cons(T &&t, Tup &&tup) {
using tuple_t = std::remove_cvref_t<Tup>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::tuple<std::remove_cvref_t<T>,
stdx::tuple_element_t<Is, tuple_t>...>{
std::forward<T>(t), std::forward<Tup>(tup)[index<Is>]...};
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
}
template <tuplelike Tup, typename T>
[[nodiscard]] constexpr auto tuple_snoc(Tup &&tup, T &&t) {
using tuple_t = std::remove_cvref_t<Tup>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::tuple<stdx::tuple_element_t<Is, tuple_t>...,
std::remove_cvref_t<T>>{
std::forward<Tup>(tup)[index<Is>]..., std::forward<T>(t)};
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
}
template <typename T, tuplelike Tup>
[[nodiscard]] constexpr auto tuple_push_front(T &&t,
Tup &&tup) -> decltype(auto) {
return tuple_cons(std::forward<T>(t), std::forward<Tup>(tup));
}
template <tuplelike Tup, typename T>
[[nodiscard]] constexpr auto tuple_push_back(Tup &&tup,
T &&t) -> decltype(auto) {
return tuple_snoc(std::forward<Tup>(tup), std::forward<T>(t));
}
template <template <typename T> typename Pred, tuplelike T>
[[nodiscard]] constexpr auto filter(T &&t) {
using tuple_t = std::remove_cvref_t<T>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
constexpr auto num_matches =
(std::size_t{} + ... +
(Pred<stdx::tuple_element_t<Is, tuple_t>>::value ? std::size_t{1}
: std::size_t{}));
constexpr auto indices = [] {
auto a = std::array<std::size_t, num_matches>{};
[[maybe_unused]] auto it = a.begin();
[[maybe_unused]] auto copy_index =
[&]<std::size_t I, typename Elem> {
if constexpr (Pred<Elem>::value) {
*it++ = I;
}
};
(copy_index
.template operator()<Is, stdx::tuple_element_t<Is, tuple_t>>(),
...);
return a;
}();
return [&]<std::size_t... Js>(std::index_sequence<Js...>) {
using R =
stdx::tuple<stdx::tuple_element_t<indices[Js], tuple_t>...>;
return R{std::forward<T>(t)[index<indices[Js]>]...};
}(std::make_index_sequence<num_matches>{});
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t>>{});
}
namespace detail {
template <std::size_t I, typename... Ts>
constexpr auto invoke_at(auto &&op, Ts &&...ts) -> decltype(auto) {
return op(std::forward<Ts>(ts)[index<I>]...);
}
template <typename... Ts>
constexpr std::size_t zip_length_for =
sizeof...(Ts) == 0
? 0
: std::min({stdx::tuple_size_v<std::remove_cvref_t<Ts>>...});
} // namespace detail
template <template <typename> typename... Fs, typename Op, tuplelike... Ts>
constexpr auto transform(Op &&op, Ts &&...ts) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
if constexpr (sizeof...(Fs) == 0) {
return stdx::tuple<decltype(detail::invoke_at<Is>(
std::forward<Op>(op), std::forward<Ts>(ts)...))...>{
detail::invoke_at<Is>(std::forward<Op>(op),
std::forward<Ts>(ts)...)...};
} else {
return stdx::make_indexed_tuple<Fs...>(detail::invoke_at<Is>(
std::forward<Op>(op), std::forward<Ts>(ts)...)...);
}
}(std::make_index_sequence<detail::zip_length_for<Ts...>>{});
}
template <typename Op, typename... Ts>
constexpr auto unrolled_for_each(Op &&op, Ts &&...ts) -> Op {
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
(detail::invoke_at<Is>(op, std::forward<Ts>(ts)...), ...);
}(std::make_index_sequence<detail::zip_length_for<Ts...>>{});
return op;
}
template <typename Op, tuplelike... Ts>
constexpr auto for_each(Op &&op, Ts &&...ts) -> Op {
return unrolled_for_each(std::forward<Op>(op), std::forward<Ts>(ts)...);
}
namespace detail {
template <std::size_t I, typename... Ts>
constexpr auto invoke_with_idx_at(auto &&op, Ts &&...ts) -> decltype(auto) {
return op.template operator()<I>(std::forward<Ts>(ts)[index<I>]...);
}
} // namespace detail
template <typename Op, typename... Ts>
constexpr auto unrolled_enumerate(Op &&op, Ts &&...ts) -> Op {
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
(detail::invoke_with_idx_at<Is>(op, std::forward<Ts>(ts)...), ...);
}(std::make_index_sequence<detail::zip_length_for<Ts...>>{});
return op;
}
template <typename Op, tuplelike... Ts>
constexpr auto enumerate(Op &&op, Ts &&...ts) -> Op {
return unrolled_enumerate(std::forward<Op>(op), std::forward<Ts>(ts)...);
}
template <typename F, tuplelike... Ts>
constexpr auto all_of(F &&f, Ts &&...ts) -> bool {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return (... and detail::invoke_at<Is>(f, std::forward<Ts>(ts)...));
}(std::make_index_sequence<detail::zip_length_for<Ts...>>{});
}
template <typename F, tuplelike... Ts>
constexpr auto any_of(F &&f, Ts &&...ts) -> bool {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return (... or detail::invoke_at<Is>(f, std::forward<Ts>(ts)...));
}(std::make_index_sequence<detail::zip_length_for<Ts...>>{});
}
template <typename... Ts> constexpr auto none_of(Ts &&...ts) -> bool {
return not any_of(std::forward<Ts>(ts)...);
}
namespace detail {
template <typename T, template <typename> typename F, typename... Us>
constexpr auto is_index_for = (std::is_same_v<F<Us>, T> or ...);
template <typename T, typename IndexSeq, template <typename> typename... Fs,
typename... Us>
constexpr auto contains_type(
stdx::detail::tuple_impl<IndexSeq, index_function_list<Fs...>, Us...> const
&) -> std::bool_constant<(is_index_for<T, Fs, Us...> or ...) or
(std::is_same_v<T, Us> or ...)>;
template <tuplelike T, template <typename> typename Proj = std::type_identity_t>
[[nodiscard]] constexpr auto sorted_indices() {
return []<std::size_t... Is>(std::index_sequence<Is...>)
-> std::array<std::size_t, sizeof...(Is)> {
using P = std::pair<std::string_view, std::size_t>;
auto a = std::array<P, sizeof...(Is)>{
P{stdx::type_as_string<Proj<tuple_element_t<Is, T>>>(), Is}...};
std::sort(a.begin(), a.end(), [](auto const &p1, auto const &p2) {
return p1.first < p2.first;
});
return {a[Is].second...};
}
(std::make_index_sequence<T::size()>{});
}
} // namespace detail
template <tuplelike Tuple, typename T>
constexpr auto contains_type =
decltype(detail::contains_type<T>(std::declval<Tuple>()))::value;
template <template <typename> typename Proj = std::type_identity_t,
tuplelike Tuple>
[[nodiscard]] constexpr auto sort(Tuple &&t) {
using T = stdx::remove_cvref_t<Tuple>;
constexpr auto indices = detail::sorted_indices<T, Proj>();
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::tuple<tuple_element_t<indices[Is], T>...>{
std::forward<Tuple>(t)[index<indices[Is]>]...};
}(std::make_index_sequence<T::size()>{});
}
namespace detail {
template <tuplelike T, template <typename> typename Proj> struct test_pair_t {
template <std::size_t I, std::size_t J>
constexpr static auto value =
std::is_same_v<Proj<stdx::tuple_element_t<I, T>>,
Proj<stdx::tuple_element_t<J, T>>>;
};
template <tuplelike T, template <typename> typename Proj = std::type_identity_t>
requires(tuple_size_v<T> > 1)
[[nodiscard]] constexpr auto count_chunks() {
auto count = std::size_t{1};
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
((count += static_cast<std::size_t>(
not test_pair_t<T, Proj>::template value<Is, Is + 1>)),
...);
}(std::make_index_sequence<stdx::tuple_size_v<T> - 1>{});
return count;
}
struct chunk {
std::size_t offset{};
std::size_t size{};
friend constexpr auto operator==(chunk const &,
chunk const &) -> bool = default;
};
template <tuplelike T, template <typename> typename Proj = std::type_identity_t>
requires(tuple_size_v<T> > 1)
[[nodiscard]] constexpr auto create_chunks() {
auto index = std::size_t{};
std::array<chunk, count_chunks<T, Proj>()> chunks{};
++chunks[index].size;
auto check_next_chunk = [&]<std::size_t I>() {
if (not test_pair_t<T, Proj>::template value<I, I + 1>) {
chunks[++index].offset = I + 1;
}
++chunks[index].size;
};
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
(check_next_chunk.template operator()<Is>(), ...);
}(std::make_index_sequence<stdx::tuple_size_v<T> - 1>{});
return chunks;
}
} // namespace detail
template <template <typename> typename Proj = std::type_identity_t,
tuplelike Tuple>
[[nodiscard]] constexpr auto chunk_by(Tuple &&t) {
using tuple_t = std::remove_cvref_t<Tuple>;
if constexpr (tuple_size_v<tuple_t> == 0) {
return stdx::tuple{};
} else if constexpr (tuple_size_v<tuple_t> == 1) {
return stdx::make_tuple(std::forward<Tuple>(t));
} else {
constexpr auto chunks = detail::create_chunks<tuple_t, Proj>();
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::make_tuple(
[&]<std::size_t... Js>(std::index_sequence<Js...>) {
constexpr auto offset = chunks[Is].offset;
return stdx::tuple<tuple_element_t<
offset + Js, stdx::remove_cvref_t<Tuple>>...>{
std::forward<Tuple>(t)[index<offset + Js>]...};
}(std::make_index_sequence<chunks[Is].size>{})...);
}(std::make_index_sequence<chunks.size()>{});
}
}
template <tuplelike Tuple> [[nodiscard]] constexpr auto chunk(Tuple &&t) {
return chunk_by(std::forward<Tuple>(t));
}
template <tuplelike... Ts> constexpr auto cartesian_product_copy(Ts &&...ts) {
if constexpr (sizeof...(Ts) == 0) {
return make_tuple(tuple{});
} else {
return []<typename First, typename... Rest>(First &&first,
Rest &&...rest) {
auto const c = cartesian_product_copy(std::forward<Rest>(rest)...);
return std::forward<First>(first).apply([&]<typename... Elems>(
Elems &&...elems) {
[[maybe_unused]] auto const prepend = [&]<typename E>(E &&e) {
return c.apply([&](auto... subs) {
return make_tuple(
tuple_cat(make_tuple(std::forward<E>(e)), subs)...);
});
};
return tuple_cat(prepend(std::forward<Elems>(elems))...);
});
}(std::forward<Ts>(ts)...);
}
}
template <tuplelike... Ts> constexpr auto cartesian_product(Ts &&...ts) {
if constexpr (sizeof...(Ts) == 0) {
return make_tuple(tuple{});
} else {
return []<typename First, typename... Rest>(First &&first,
Rest &&...rest) {
auto const c = cartesian_product(std::forward<Rest>(rest)...);
return std::forward<First>(first).apply(
[&]<typename... Elems>(Elems &&...elems) {
auto const prepend = [&]<typename E>(E &&e) {
return c.apply([&](auto... subs) {
return make_tuple(tuple_cat(
forward_as_tuple(std::forward<E>(e)), subs)...);
});
};
return tuple_cat(prepend(std::forward<Elems>(elems))...);
});
}(std::forward<Ts>(ts)...);
}
}
template <tuplelike T> constexpr auto unique(T &&t) {
return chunk(std::forward<T>(t)).apply([]<typename... Us>(Us &&...us) {
return tuple<tuple_element_t<0, Us>...>{
get<0>(std::forward<Us>(us))...};
});
}
template <tuplelike T> constexpr auto to_sorted_set(T &&t) {
return unique(sort(std::forward<T>(t)));
}
template <tuplelike Tuple> constexpr auto to_unsorted_set(Tuple &&t) {
using T = stdx::remove_cvref_t<Tuple>;
using U = boost::mp11::mp_unique<T>;
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return U{get<boost::mp11::mp_find<T, tuple_element_t<Is, U>>::value>(
std::forward<Tuple>(t))...};
}(std::make_index_sequence<U::size()>{});
}
template <template <typename> typename Proj = std::type_identity_t,
tuplelike Tuple>
[[nodiscard]] constexpr auto gather_by(Tuple &&t) {
using tuple_t = std::remove_cvref_t<Tuple>;
if constexpr (tuple_size_v<tuple_t> == 0) {
return stdx::tuple{};
} else if constexpr (tuple_size_v<tuple_t> == 1) {
return stdx::make_tuple(std::forward<Tuple>(t));
} else {
constexpr auto sorted_idxs = detail::sorted_indices<tuple_t, Proj>();
constexpr auto tests =
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::array<bool, stdx::tuple_size_v<tuple_t> - 1>{
detail::test_pair_t<tuple_t, Proj>::template value<
sorted_idxs[Is], sorted_idxs[Is + 1]>...};
}(std::make_index_sequence<stdx::tuple_size_v<tuple_t> - 1>{});
constexpr auto chunks = [&] {
constexpr auto chunk_count =
std::count(std::begin(tests), std::end(tests), false) + 1;
std::array<detail::chunk, chunk_count> cs{};
auto index = std::size_t{};
++cs[index].size;
for (auto i = std::size_t{}; i < std::size(tests); ++i) {
if (not tests[i]) {
cs[++index].offset = i + 1;
}
++cs[index].size;
}
return cs;
}();
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return stdx::make_tuple([&]<std::size_t... Js>(
std::index_sequence<Js...>) {
constexpr auto offset = chunks[Is].offset;
return stdx::tuple<
tuple_element_t<sorted_idxs[offset + Js], tuple_t>...>{
std::forward<Tuple>(t)[index<sorted_idxs[offset + Js]>]...};
}(std::make_index_sequence<chunks[Is].size>{})...);
}(std::make_index_sequence<chunks.size()>{});
}
}
template <tuplelike Tuple> [[nodiscard]] constexpr auto gather(Tuple &&t) {
return gather_by(std::forward<Tuple>(t));
}
} // namespace v1
} // namespace stdx
#endif

View File

@@ -0,0 +1,29 @@
#pragma once
#if __cplusplus >= 202002L
#include <stdx/tuple.hpp>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template <typename... Ts>
struct std::tuple_size<stdx::tuple<Ts...>>
: std::integral_constant<std::size_t, sizeof...(Ts)> {};
template <std::size_t I, typename... Ts>
struct std::tuple_element<I, stdx::tuple<Ts...>>
: std::type_identity<std::remove_cvref_t<
decltype(std::declval<stdx::tuple<Ts...>>()[stdx::index<I>])>> {};
template <typename IL, typename... Ts>
struct std::tuple_size<stdx::indexed_tuple<IL, Ts...>>
: std::integral_constant<std::size_t, sizeof...(Ts)> {};
template <std::size_t I, typename IL, typename... Ts>
struct std::tuple_element<I, stdx::indexed_tuple<IL, Ts...>>
: std::type_identity<
std::remove_cvref_t<decltype(std::declval<stdx::indexed_tuple<
IL, Ts...>>()[stdx::index<I>])>> {};
#endif

View File

@@ -0,0 +1,231 @@
#pragma once
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <typename E> constexpr auto to_underlying(E e) noexcept {
if constexpr (std::is_enum_v<E>) {
return static_cast<std::underlying_type_t<E>>(e);
} else {
return e;
}
}
template <typename E>
using underlying_type_t = decltype(to_underlying(std::declval<E>()));
template <typename T> struct remove_cvref {
using type = std::remove_cv_t<std::remove_reference_t<T>>;
};
template <typename T> using remove_cvref_t = typename remove_cvref<T>::type;
namespace detail {
template <bool> struct conditional;
template <> struct conditional<true> {
template <typename T, typename> using choice_t = T;
};
template <> struct conditional<false> {
template <typename, typename U> using choice_t = U;
};
} // namespace detail
template <bool B, typename T, typename U>
using conditional_t = typename detail::conditional<B>::template choice_t<T, U>;
template <template <typename...> typename P, typename X, typename Y = void>
using type_or_t = conditional_t<P<X>::value, X, Y>;
template <typename...> constexpr bool always_false_v = false;
template <typename T>
constexpr bool is_function_v =
not std::is_reference_v<T> and not std::is_const_v<std::add_const_t<T>>;
namespace detail {
struct call_base {
auto operator()() -> void;
};
template <typename, bool> struct callable_test : call_base {};
template <typename F> struct callable_test<F, true> : F, call_base {};
template <typename F, typename = void> constexpr auto is_func_obj = true;
template <typename F>
constexpr auto is_func_obj<
F,
std::void_t<decltype(&callable_test<F, std::is_class_v<F>>::operator())>> =
false;
} // namespace detail
template <typename T>
constexpr bool is_function_object_v = detail::is_func_obj<T>;
template <typename T>
constexpr bool is_callable_v = is_function_v<T> or is_function_object_v<T>;
constexpr auto is_constant_evaluated() noexcept -> bool {
return __builtin_is_constant_evaluated();
}
template <typename T> struct type_identity {
using type = T;
};
template <typename T> using type_identity_t = typename type_identity<T>::type;
namespace detail {
template <typename T, template <typename...> typename U>
constexpr bool is_type_specialization_of_v = false;
template <typename... Ts, template <typename...> typename U>
constexpr bool is_type_specialization_of_v<U<Ts...> &, U> = true;
template <typename... Ts, template <typename...> typename U>
constexpr bool is_type_specialization_of_v<U<Ts...> const &, U> = true;
template <typename T, template <auto...> typename U>
constexpr bool is_value_specialization_of_v = false;
template <auto... Vs, template <auto...> typename U>
constexpr bool is_value_specialization_of_v<U<Vs...> &, U> = true;
template <auto... Vs, template <auto...> typename U>
constexpr bool is_value_specialization_of_v<U<Vs...> const &, U> = true;
} // namespace detail
template <typename U, template <typename...> typename T>
constexpr bool is_specialization_of_v =
detail::is_type_specialization_of_v<U &, T>;
template <typename U, template <typename...> typename T>
constexpr bool is_type_specialization_of_v =
detail::is_type_specialization_of_v<U &, T>;
template <typename U, template <auto...> typename T>
constexpr bool is_value_specialization_of_v =
detail::is_value_specialization_of_v<U &, T>;
template <typename U, template <typename...> typename T>
constexpr auto
is_specialization_of() -> std::bool_constant<is_specialization_of_v<U, T>> {
return {};
}
template <typename U, template <auto...> typename T>
constexpr auto is_specialization_of()
-> std::bool_constant<is_value_specialization_of_v<U, T>> {
return {};
}
template <typename E>
constexpr bool is_scoped_enum_v =
std::is_enum_v<E> and not std::is_convertible_v<E, underlying_type_t<E>>;
template <typename E>
using is_scoped_enum = std::bool_constant<is_scoped_enum_v<E>>;
template <typename...> struct type_list {};
template <auto...> struct value_list {};
namespace detail {
template <typename L> struct for_each_t {
static_assert(always_false_v<L>,
"template_for_each must be called with a type list, "
"value_list, or std::integer_sequence");
};
template <template <typename...> typename L, typename... Ts>
struct for_each_t<L<Ts...>> {
template <typename F> constexpr auto operator()(F &&f) const {
(f.template operator()<Ts>(), ...);
}
};
template <template <auto...> typename L, auto... Vs>
struct for_each_t<L<Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
(f.template operator()<Vs>(), ...);
}
};
template <template <typename, auto...> typename L, typename T, T... Vs>
struct for_each_t<L<T, Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
(f.template operator()<Vs>(), ...);
}
};
} // namespace detail
template <typename L>
constexpr static auto template_for_each = detail::for_each_t<L>{};
namespace detail {
template <typename L> struct apply_sequence_t {
static_assert(always_false_v<L>,
"apply_sequence must be called with a type list, "
"value_list, or std::integer_sequence");
};
template <template <typename...> typename L, typename... Ts>
struct apply_sequence_t<L<Ts...>> {
template <typename F> constexpr auto operator()(F &&f) const {
return f.template operator()<Ts...>();
}
};
template <template <auto...> typename L, auto... Vs>
struct apply_sequence_t<L<Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
return f.template operator()<Vs...>();
}
};
template <template <typename, auto...> typename L, typename T, T... Vs>
struct apply_sequence_t<L<T, Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
return f.template operator()<Vs...>();
}
};
} // namespace detail
template <typename L>
constexpr static auto apply_sequence = detail::apply_sequence_t<L>{};
template <typename T, typename U>
constexpr bool is_same_unqualified_v =
std::is_same_v<remove_cvref_t<T>, remove_cvref_t<U>>;
namespace detail {
template <typename T> struct any_t;
template <typename T, typename... Ts> constexpr auto try_construct() -> T {
if constexpr (std::is_constructible_v<T, Ts...>) {
return T{Ts{}...};
} else if constexpr (sizeof...(Ts) < 10) {
return try_construct<T, Ts..., any_t<T>>();
} else {
throw;
}
}
template <typename T> struct any_t {
// NOLINTNEXTLINE(modernize-use-constraints)
template <typename U, std::enable_if_t<not std::is_same_v<T, U>, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator U() {
return try_construct<U>();
}
};
template <auto> using void_v = void;
template <typename T, typename = void> constexpr auto detect_structural = false;
template <typename T> constexpr auto detect_structural<T &, void> = true;
template <typename T>
constexpr auto detect_structural<T, void_v<try_construct<T>()>> = true;
} // namespace detail
template <typename T>
constexpr bool is_structural_v = detail::detect_structural<T>;
template <typename T, typename = void> constexpr auto is_cx_value_v = false;
template <typename T>
constexpr auto is_cx_value_v<T, std::void_t<typename T::cx_value_t>> = true;
#if __cplusplus >= 202002L
template <typename T>
using shrink_t = decltype([]() -> T (*)() { return nullptr; });
template <typename T> using expand_t = decltype(T{}()());
#endif
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,167 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/type_traits.hpp>
#include <cstddef>
#include <cstdint>
#include <type_traits>
namespace stdx {
inline namespace v1 {
namespace detail {
template <char C> constexpr static bool is_digit_sep_v = C == '\'';
template <char C>
constexpr static bool is_decimal_digit_v = C >= '0' and C <= '9';
template <char C>
constexpr static bool is_octal_digit_v = C >= '0' and C <= '7';
template <char C>
constexpr static bool is_binary_digit_v = C >= '0' and C <= '1';
template <char C>
constexpr static char force_lower_case = static_cast<unsigned char>(C) | 32u;
template <char C>
constexpr static bool is_hex_digit_v =
(C >= '0' and C <= '9') or
(force_lower_case<C> >= 'a' and force_lower_case<C> <= 'f');
template <char C>
constexpr static auto integral_value_v =
is_decimal_digit_v<C> ? C - '0' : force_lower_case<C> - 'a' + 10;
template <auto Base, char C, typename Sum>
CONSTEVAL auto maybe_add_digit(Sum s) {
if constexpr (not is_digit_sep_v<C>) {
s *= Base;
s += integral_value_v<C>;
}
return s;
}
template <auto Base, char... Cs> struct raw_parser {
template <typename T> CONSTEVAL static auto parse() {
using U = decltype(stdx::to_underlying(std::declval<T>()));
auto x = U{};
((x = maybe_add_digit<Base, Cs>(x)), ...);
return T{x};
}
};
template <char... Cs> struct parser : raw_parser<10, Cs...> {
static_assert((... and (is_decimal_digit_v<Cs> or is_digit_sep_v<Cs>)));
};
template <char... Cs> struct parser<'0', Cs...> : raw_parser<8, Cs...> {
static_assert((... and (is_octal_digit_v<Cs> or is_digit_sep_v<Cs>)));
};
template <char... Cs> struct parser<'0', 'x', Cs...> : raw_parser<16, Cs...> {
static_assert((... and (is_hex_digit_v<Cs> or is_digit_sep_v<Cs>)));
};
template <char... Cs>
struct parser<'0', 'X', Cs...> : parser<'0', 'x', Cs...> {};
template <char... Cs> struct parser<'0', 'b', Cs...> : raw_parser<2, Cs...> {
static_assert((... and (is_binary_digit_v<Cs> or is_digit_sep_v<Cs>)));
};
template <char... Cs>
struct parser<'0', 'B', Cs...> : parser<'0', 'b', Cs...> {};
} // namespace detail
template <typename T, char... Chars> CONSTEVAL auto parse_literal() -> T {
using parser_t = detail::parser<Chars...>;
return parser_t::template parse<T>();
}
template <auto I> using constant = std::integral_constant<decltype(I), I>;
template <auto I> constexpr static constant<I> _c{};
inline namespace literals {
template <char... Chars> CONSTEVAL auto operator""_c() {
return _c<parse_literal<std::uint32_t, Chars...>()>;
}
} // namespace literals
enum struct lsb_t : std::uint32_t {};
enum struct msb_t : std::uint32_t {};
enum struct length_t : std::uint32_t {};
inline namespace literals {
// NOLINTBEGIN(google-runtime-int)
CONSTEVAL auto operator""_lsb(unsigned long long int n) -> lsb_t {
return static_cast<lsb_t>(n);
}
CONSTEVAL auto operator""_msb(unsigned long long int n) -> msb_t {
return static_cast<msb_t>(n);
}
CONSTEVAL auto operator""_len(unsigned long long int n) -> length_t {
return static_cast<length_t>(n);
}
// NOLINTEND(google-runtime-int)
} // namespace literals
inline namespace literals {
CONSTEVAL auto operator""_b(char const *, std::size_t) -> bool { return true; }
CONSTEVAL auto operator""_true(char const *, std::size_t) -> bool {
return true;
}
CONSTEVAL auto operator""_false(char const *, std::size_t) -> bool {
return false;
}
// NOLINTBEGIN(google-runtime-int)
CONSTEVAL auto
operator""_k(unsigned long long int n) -> unsigned long long int {
return n * 1'000u;
}
CONSTEVAL auto
operator""_M(unsigned long long int n) -> unsigned long long int {
return n * 1'000'000u;
}
CONSTEVAL auto
operator""_G(unsigned long long int n) -> unsigned long long int {
return n * 1'000'000'000u;
}
CONSTEVAL auto
operator""_ki(unsigned long long int n) -> unsigned long long int {
return n * 1'024u;
}
CONSTEVAL auto
operator""_Mi(unsigned long long int n) -> unsigned long long int {
return n * 1'024ull * 1'024ull;
}
CONSTEVAL auto
operator""_Gi(unsigned long long int n) -> unsigned long long int {
return n * 1'024ull * 1'024ull * 1'024ull;
}
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define STDX_SMALL_INT_LITERAL_DEF(x) \
CONSTEVAL auto operator""_##x(char const *, std::size_t) \
->std::integral_constant<std::size_t, x##u> { \
return {}; \
}
STDX_SMALL_INT_LITERAL_DEF(0)
STDX_SMALL_INT_LITERAL_DEF(1)
STDX_SMALL_INT_LITERAL_DEF(2)
STDX_SMALL_INT_LITERAL_DEF(3)
STDX_SMALL_INT_LITERAL_DEF(4)
STDX_SMALL_INT_LITERAL_DEF(5)
STDX_SMALL_INT_LITERAL_DEF(6)
STDX_SMALL_INT_LITERAL_DEF(7)
STDX_SMALL_INT_LITERAL_DEF(8)
STDX_SMALL_INT_LITERAL_DEF(9)
#undef STDX_SMALL_INT_LITERAL_DEF
// NOLINTEND(cppcoreguidelines-macro-usage)
// NOLINTEND(google-runtime-int)
} // namespace literals
} // namespace v1
} // namespace stdx

View File

@@ -0,0 +1,248 @@
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/type_traits.hpp>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
// NOLINTBEGIN(modernize-use-constraints)
namespace stdx {
inline namespace v1 {
template <typename... Fs> struct overload : Fs... {
using Fs::operator()...;
};
#if __cpp_deduction_guides < 201907L
template <typename... Fs> overload(Fs...) -> overload<Fs...>;
#endif
[[noreturn]] inline auto unreachable() -> void { __builtin_unreachable(); }
namespace detail {
template <auto V> struct value_t {
constexpr static inline auto value = V;
};
} // namespace detail
template <typename K, typename V> struct type_pair {};
template <typename K, typename V> using tt_pair = type_pair<K, V>;
template <auto K, typename V> using vt_pair = tt_pair<detail::value_t<K>, V>;
template <typename K, auto V> using tv_pair = tt_pair<K, detail::value_t<V>>;
template <auto K, auto V>
using vv_pair = tt_pair<detail::value_t<K>, detail::value_t<V>>;
template <typename... Ts> struct type_map : Ts... {};
namespace detail {
template <typename K, typename Default>
constexpr static auto lookup(...) -> Default;
template <typename K, typename Default, typename V>
constexpr static auto lookup(type_pair<K, V>) -> V;
} // namespace detail
template <typename M, typename K, typename Default = void>
using type_lookup_t = decltype(detail::lookup<K, Default>(std::declval<M>()));
template <typename M, auto K, typename Default = void>
using value_lookup_t =
decltype(detail::lookup<detail::value_t<K>, Default>(std::declval<M>()));
namespace detail {
template <typename T>
using is_not_void = std::bool_constant<not std::is_void_v<T>>;
}
template <typename M, typename K, auto Default = 0>
constexpr static auto type_lookup_v =
type_or_t<detail::is_not_void,
decltype(detail::lookup<K, void>(std::declval<M>())),
detail::value_t<Default>>::value;
template <typename M, auto K, auto Default = 0>
constexpr static auto value_lookup_v =
type_or_t<detail::is_not_void,
decltype(detail::lookup<detail::value_t<K>, void>(
std::declval<M>())),
detail::value_t<Default>>::value;
#if __cpp_lib_forward_like < 202207L
template <typename T, typename U>
[[nodiscard]] constexpr auto forward_like(U &&u) noexcept -> decltype(auto) {
constexpr auto t_is_const = std::is_const_v<std::remove_reference_t<T>>;
if constexpr (std::is_lvalue_reference_v<T &&>) {
if constexpr (t_is_const) {
return std::as_const(u);
} else {
return (u);
}
} else {
if constexpr (t_is_const) {
return std::move(std::as_const(u));
} else {
return static_cast<U &&>(u);
}
}
}
#else
using std::forward_like;
#endif
template <typename T, typename U>
using forward_like_t = decltype(forward_like<T>(std::declval<U>()));
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
[[nodiscard]] auto as_unsigned(T t) {
static_assert(not std::is_same_v<T, bool>,
"as_unsigned is not applicable to bool");
return static_cast<std::make_unsigned_t<T>>(t);
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
[[nodiscard]] auto as_signed(T t) {
static_assert(not std::is_same_v<T, bool>,
"as_signed is not applicable to bool");
return static_cast<std::make_signed_t<T>>(t);
}
namespace detail {
template <typename T, typename U>
[[nodiscard]] constexpr auto size_conversion(std::size_t sz) -> std::size_t {
if constexpr (sizeof(T) == sizeof(U)) {
return sz;
} else if constexpr (sizeof(T) > sizeof(U)) {
return sz * (sizeof(T) / sizeof(U));
} else {
return (sz * sizeof(T) + sizeof(U) - 1) / sizeof(U);
}
}
} // namespace detail
template <typename T> struct sized {
template <typename U = std::uint8_t>
[[nodiscard]] constexpr auto in() -> std::size_t {
return detail::size_conversion<T, U>(sz);
}
std::size_t sz;
};
using sized8 = sized<std::uint8_t>;
using sized16 = sized<std::uint16_t>;
using sized32 = sized<std::uint32_t>;
using sized64 = sized<std::uint64_t>;
namespace cxv_detail {
struct from_any {
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename... Ts> constexpr from_any(Ts const &...) {}
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator int() const { return 0; }
};
struct type_val {
template <typename T, typename U,
typename = std::enable_if_t<same_as_unqualified<type_val, U>>>
friend constexpr auto operator+(T &&t, U &&) -> T {
return t;
}
friend constexpr auto operator+(type_val const &f) -> type_val { return f; }
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename T> constexpr operator T() const {
extern auto cxv_type_val_get_t(T *) -> T;
return cxv_type_val_get_t(nullptr);
}
};
template <int> constexpr auto is_type() -> std::false_type;
template <typename> constexpr auto is_type() -> std::true_type;
template <typename> struct typer;
template <typename T> struct typer<from_any(T)> {
using type = T;
};
template <int> constexpr auto type_of() -> void;
template <typename T> constexpr auto type_of() -> typename typer<T>::type;
class cx_base {
struct unusable {};
public:
using cx_value_t [[maybe_unused]] = void;
constexpr auto operator()(unusable) const {}
};
} // namespace cxv_detail
template <typename T>
constexpr auto is_aligned_with = [](auto v) -> bool {
static_assert(std::is_integral_v<decltype(v)> or
std::is_pointer_v<decltype(v)>,
"is_aligned_with should only be used with an integral or "
"pointer argument!");
constexpr auto mask = alignof(T) - 1u;
if constexpr (std::is_pointer_v<decltype(v)>) {
return (__builtin_bit_cast(std::uintptr_t, v) & mask) == 0;
} else {
return (static_cast<std::uintptr_t>(v) & mask) == 0;
}
};
#if __cplusplus >= 202002L
namespace detail {
template <typename T> struct ct_helper {
// NOLINTNEXTLINE(google-explicit-constructor)
CONSTEVAL ct_helper(T t) : value(t) {}
T value;
};
template <typename T> ct_helper(T) -> ct_helper<T>;
} // namespace detail
template <detail::ct_helper Value> CONSTEVAL auto ct() {
return std::integral_constant<decltype(Value.value), Value.value>{};
}
template <typename T> CONSTEVAL auto ct() { return type_identity<T>{}; }
#endif
} // namespace v1
} // namespace stdx
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#ifndef FWD
#define FWD(x) std::forward<decltype(x)>(x)
#endif
#ifndef CX_VALUE
#define CX_VALUE(...) \
[]() constexpr { \
STDX_PRAGMA(diagnostic push) \
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
STDX_PRAGMA(diagnostic ignored "-Wunused-value") \
if constexpr (decltype(stdx::cxv_detail::is_type< \
stdx::cxv_detail::from_any( \
__VA_ARGS__)>())::value) { \
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
return stdx::type_identity< \
decltype(stdx::cxv_detail::type_of< \
stdx::cxv_detail::from_any( \
__VA_ARGS__)>())>{}; \
}}; \
} else { \
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
return (__VA_ARGS__) + \
stdx::cxv_detail::type_val{}; \
}}; \
} \
STDX_PRAGMA(diagnostic pop) \
}()
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
// NOLINTEND(modernize-use-constraints)