rename chapters
This commit is contained in:
509
Chapter17/cib/libs/stdx/tuple.hpp
Normal file
509
Chapter17/cib/libs/stdx/tuple.hpp
Normal 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
|
||||
Reference in New Issue
Block a user