yalantinglibs/include/ylt/struct_pack/tuple.hpp

630 lines
20 KiB
C++

/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// https://github.com/codeinred/tuplet/blob/main/include/tuplet/tuple.hpp
// commit id: ce4ab635c4f70b63ef9d19728bc8d76d71ae8685
// Use of this source code is governed by a Boost Software License that can be
// found in the LICENSE file.
#ifndef TUPLET_TUPLET_HPP_IMPLEMENTATION
#define TUPLET_TUPLET_HPP_IMPLEMENTATION
#include <compare>
#include <cstddef>
#include <string_view>
#include <type_traits>
#include <utility>
#if (__has_cpp_attribute(no_unique_address))
#define TUPLET_NO_UNIQUE_ADDRESS [[no_unique_address]]
#elif (__has_cpp_attribute(msvc::no_unique_address)) || \
((defined _MSC_VER) && (!defined __clang__))
// Note __has_cpp_attribute(msvc::no_unique_address) itself doesn't work as
// of 19.30.30709, even though the attribute itself is supported. See
// https://github.com/llvm/llvm-project/issues/49358#issuecomment-981041089
#define TUPLET_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#else
// no_unique_address is not available.
#define TUPLET_NO_UNIQUE_ADDRESS
#endif
// tuplet concepts and traits
namespace tuplet {
template <class T>
using identity_t = T;
// Obtains T::type
template <class T>
using type_t = typename T::type;
template <size_t I>
using tag = std::integral_constant<size_t, I>;
template <size_t I>
constexpr tag<I> tag_v{};
template <size_t N>
using tag_range = std::make_index_sequence<N>;
template <class T, class U>
concept same_as = std::is_same_v<T, U> && std::is_same_v<U, T>;
template <class T, class U>
concept other_than = !std::is_same_v<std::decay_t<T>, U>;
template <class Tup>
using base_list_t = typename std::decay_t<Tup>::base_list;
template <class Tup>
using element_list_t = typename std::decay_t<Tup>::element_list;
template <class Tuple>
concept base_list_tuple = requires() {
typename std::decay_t<Tuple>::base_list;
};
template <class T>
concept stateless = std::is_empty_v<std::decay_t<T>>;
template <class T>
concept indexable = stateless<T> || requires(T t) {
t[tag<0>()];
};
template <size_t I, indexable Tup>
constexpr decltype(auto) get(Tup&& tup);
template <class T, class U>
concept other_than_tuple =
!std::is_same_v<std::decay_t<T>, U> && (requires(U u) { get<U>(); });
template <class U, class T>
concept assignable_to = requires(U u, T t) {
t = u;
};
template <class T>
concept ordered = requires(T const& t) {
{t <=> t};
};
template <class T>
concept equality_comparable = requires(T const& t) {
{ t == t } -> same_as<bool>;
};
} // namespace tuplet
// tuplet::type_list implementation
// tuplet::type_map implementation
// tuplet::tuple_elem implementation
// tuplet::deduce_elems
namespace tuplet {
template <class... T>
struct tuple;
template <class... T>
struct type_list {};
template <class... Ls, class... Rs>
constexpr auto operator+(type_list<Ls...>, type_list<Rs...>) {
return type_list<Ls..., Rs...>{};
}
template <class... Bases>
struct type_map : Bases... {
using base_list = type_list<Bases...>;
using Bases::operator[]...;
using Bases::decl_elem...;
auto operator<=>(type_map const&) const = default;
bool operator==(type_map const&) const = default;
};
template <size_t I, class T>
struct tuple_elem {
// Like declval, but with the element
static T decl_elem(tag<I>);
using type = T;
TUPLET_NO_UNIQUE_ADDRESS T value;
constexpr decltype(auto) operator[](tag<I>) & { return (value); }
constexpr decltype(auto) operator[](tag<I>) const& { return (value); }
constexpr decltype(auto) operator[](tag<I>) && {
return (std::move(*this).value);
}
auto operator<=>(tuple_elem const&) const = default;
bool operator==(tuple_elem const&) const = default;
// Implements comparison for tuples containing reference types
constexpr auto operator<=>(tuple_elem const& other) const noexcept(noexcept(
value <=> other.value)) requires(std::is_reference_v<T>&& ordered<T>) {
return value <=> other.value;
}
constexpr bool operator==(tuple_elem const& other) const
noexcept(noexcept(value == other.value)) requires(
std::is_reference_v<T>&& equality_comparable<T>) {
return value == other.value;
}
};
template <class T>
using unwrap_ref_decay_t = typename std::unwrap_ref_decay<T>::type;
} // namespace tuplet
// tuplet::detail::get_tuple_base implementation
// tuplet::detail::apply_impl
// tuplet::detail::size_t_from_digits
namespace tuplet::detail {
template <class A, class... T>
struct get_tuple_base;
template <size_t... I, class... T>
struct get_tuple_base<std::index_sequence<I...>, T...> {
using type = type_map<tuple_elem<I, T>...>;
};
template <class F, class T, class... Bases>
constexpr decltype(auto) apply_impl(F&& f, T&& t, type_list<Bases...>) {
return static_cast<F&&>(f)(static_cast<T&&>(t).identity_t<Bases>::value...);
}
template <char... D>
constexpr size_t size_t_from_digits() {
static_assert((('0' <= D && D <= '9') && ...), "Must be integral literal");
size_t num = 0;
return ((num = num * 10 + (D - '0')), ..., num);
}
template <class First, class>
using first_t = First;
template <class T, class... Q>
constexpr auto repeat_type(type_list<Q...>) {
return type_list<first_t<T, Q>...>{};
}
template <class... Outer>
constexpr auto get_outer_bases(type_list<Outer...>) {
return (repeat_type<Outer>(base_list_t<type_t<Outer>>{}) + ...);
}
template <class... Outer>
constexpr auto get_inner_bases(type_list<Outer...>) {
return (base_list_t<type_t<Outer>>{} + ...);
}
// This takes a forwarding tuple as a parameter. The forwarding tuple only
// contains references, so it should just be taken by value.
template <class T, class... Outer, class... Inner>
constexpr auto cat_impl(T tup, type_list<Outer...>, type_list<Inner...>)
-> tuple<type_t<Inner>...> {
return {static_cast<type_t<Outer>&&>(tup.identity_t<Outer>::value)
.identity_t<Inner>::value...};
}
} // namespace tuplet::detail
// tuplet::tuple implementation
namespace tuplet {
template <class... T>
using tuple_base_t =
typename detail::get_tuple_base<tag_range<sizeof...(T)>, T...>::type;
template <class... T>
struct tuple : tuple_base_t<T...> {
constexpr static size_t N = sizeof...(T);
using super = tuple_base_t<T...>;
using super::operator[];
using base_list = typename super::base_list;
using element_list = type_list<T...>;
using super::decl_elem;
template <other_than_tuple<tuple> U> // Preserves default assignments
constexpr auto& operator=(U&& tup) {
using tuple2 = std::decay_t<U>;
if constexpr (base_list_tuple<tuple2>) {
eq_impl(static_cast<U&&>(tup), base_list(), typename tuple2::base_list());
}
else {
eq_impl(static_cast<U&&>(tup), tag_range<N>());
}
return *this;
}
template <assignable_to<T>... U>
constexpr auto& assign(U&&... values) {
assign_impl(base_list(), static_cast<U&&>(values)...);
return *this;
}
auto operator<=>(tuple const&) const = default;
bool operator==(tuple const&) const = default;
// Applies a function to every element of the tuple. The order is the
// declaration order, so first the function will be applied to element 0,
// then element 1, then element 2, and so on, where element N is identified
// by get<N>
template <class F>
constexpr void for_each(F&& func) & {
for_each_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr void for_each(F&& func) const& {
for_each_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr void for_each(F&& func) && {
static_cast<tuple&&>(*this).for_each_impl(base_list(),
static_cast<F&&>(func));
}
// Applies a function to each element successively, until one returns a
// truthy value. Returns true if any application returned a truthy value,
// and false otherwise
template <class F>
constexpr bool any(F&& func) & {
return any_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr bool any(F&& func) const& {
return any_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr bool any(F&& func) && {
return static_cast<tuple&&>(*this).any_impl(base_list(),
static_cast<F&&>(func));
}
// Applies a function to each element successively, until one returns a
// falsy value. Returns true if every application returned a truthy value,
// and false otherwise
template <class F>
constexpr bool all(F&& func) & {
return all_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr bool all(F&& func) const& {
return all_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr bool all(F&& func) && {
return static_cast<tuple&&>(*this).all_impl(base_list(),
static_cast<F&&>(func));
}
// Map a function over every element in the tuple, using the values to
// construct a new tuple
template <class F>
constexpr auto map(F&& func) & {
return map_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr auto map(F&& func) const& {
return map_impl(base_list(), static_cast<F&&>(func));
}
template <class F>
constexpr auto map(F&& func) && {
return static_cast<tuple&&>(*this).map_impl(base_list(),
static_cast<F&&>(func));
}
private:
template <class U, class... B1, class... B2>
constexpr void eq_impl(U&& u, type_list<B1...>, type_list<B2...>) {
// See:
// https://developercommunity.visualstudio.com/t/fold-expressions-unreliable-in-171-with-c20/1676476
(void(B1::value = static_cast<U&&>(u).B2::value), ...);
}
template <class U, size_t... I>
constexpr void eq_impl(U&& u, std::index_sequence<I...>) {
(void(tuple_elem<I, T>::value = get<I>(static_cast<U&&>(u))), ...);
}
template <class... U, class... B>
constexpr void assign_impl(type_list<B...>, U&&... u) {
(void(B::value = static_cast<U&&>(u)), ...);
}
template <class F, class... B>
constexpr void for_each_impl(type_list<B...>, F&& func) & {
(void(func(B::value)), ...);
}
template <class F, class... B>
constexpr void for_each_impl(type_list<B...>, F&& func) const& {
(void(func(B::value)), ...);
}
template <class F, class... B>
constexpr void for_each_impl(type_list<B...>, F&& func) && {
(void(func(static_cast<T&&>(B::value))), ...);
}
template <class F, class... B>
constexpr bool any_impl(type_list<B...>, F&& func) & {
return (bool(func(B::value)) || ...);
}
template <class F, class... B>
constexpr bool any_impl(type_list<B...>, F&& func) const& {
return (bool(func(B::value)) || ...);
}
template <class F, class... B>
constexpr bool any_impl(type_list<B...>, F&& func) && {
return (bool(func(static_cast<T&&>(B::value))) || ...);
}
template <class F, class... B>
constexpr bool all_impl(type_list<B...>, F&& func) & {
return (bool(func(B::value)) && ...);
}
template <class F, class... B>
constexpr bool all_impl(type_list<B...>, F&& func) const& {
return (bool(func(B::value)) && ...);
}
template <class F, class... B>
constexpr bool all_impl(type_list<B...>, F&& func) && {
return (bool(func(static_cast<T&&>(B::value))) && ...);
}
template <class F, class... B>
constexpr auto map_impl(
type_list<B...>,
F&& func) & -> tuple<unwrap_ref_decay_t<decltype(func(B::value))>...> {
return {func(B::value)...};
}
template <class F, class... B>
constexpr auto map_impl(type_list<B...>, F&& func)
const& -> tuple<unwrap_ref_decay_t<decltype(func(B::value))>...> {
return {func(B::value)...};
}
template <class F, class... B>
constexpr auto map_impl(type_list<B...>, F&& func) && -> tuple<
unwrap_ref_decay_t<decltype(func(static_cast<T&&>(B::value)))>...> {
return {func(static_cast<T&&>(B::value))...};
}
};
template <>
struct tuple<> : tuple_base_t<> {
constexpr static size_t N = 0;
using super = tuple_base_t<>;
using base_list = type_list<>;
using element_list = type_list<>;
template <other_than<tuple> U> // Preserves default assignments
requires stateless<U> // Check that U is similarly stateless
constexpr auto& operator=(U&&) noexcept { return *this; }
constexpr auto& assign() noexcept { return *this; }
auto operator<=>(tuple const&) const = default;
bool operator==(tuple const&) const = default;
// Applies a function to every element of the tuple. The order is the
// declaration order, so first the function will be applied to element 0,
// then element 1, then element 2, and so on, where element N is identified
// by get<N>
//
// (Does nothing when invoked on empty tuple)
template <class F>
constexpr void for_each(F&&) const noexcept {}
// Applies a function to each element successively, until one returns a
// truthy value. Returns true if any application returned a truthy value,
// and false otherwise
//
// (Returns false for empty tuple)
template <class F>
constexpr bool any(F&&) const noexcept {
return false;
}
// Applies a function to each element successively, until one returns a
// falsy value. Returns true if every application returned a truthy value,
// and false otherwise
//
// (Returns true for empty tuple)
template <class F>
constexpr bool all(F&&) const noexcept {
return true;
}
// Map a function over every element in the tuple, using the values to
// construct a new tuple
//
// (Returns empty tuple when invoked on empty tuple)
template <class F>
constexpr auto map(F&&) const noexcept {
return tuple{};
}
};
template <class... Ts>
tuple(Ts...) -> tuple<unwrap_ref_decay_t<Ts>...>;
} // namespace tuplet
// tuplet::pair implementation
namespace tuplet {
template <class First, class Second>
struct pair {
constexpr static size_t N = 2;
TUPLET_NO_UNIQUE_ADDRESS First first;
TUPLET_NO_UNIQUE_ADDRESS Second second;
constexpr decltype(auto) operator[](tag<0>) & { return (first); }
constexpr decltype(auto) operator[](tag<0>) const& { return (first); }
constexpr decltype(auto) operator[](tag<0>) && {
return (std::move(*this).first);
}
constexpr decltype(auto) operator[](tag<1>) & { return (second); }
constexpr decltype(auto) operator[](tag<1>) const& { return (second); }
constexpr decltype(auto) operator[](tag<1>) && {
return (std::move(*this).second);
}
template <other_than<pair> Type> // Preserves default assignments
constexpr auto& operator=(Type&& tup) {
auto&& [a, b] = static_cast<Type&&>(tup);
first = static_cast<decltype(a)&&>(a);
second = static_cast<decltype(b)&&>(b);
return *this;
}
template <assignable_to<First> F2, assignable_to<Second> S2>
constexpr auto& assign(F2&& f, S2&& s) {
first = static_cast<F2&&>(f);
second = static_cast<S2&&>(s);
return *this;
}
auto operator<=>(pair const&) const = default;
bool operator==(pair const&) const = default;
};
template <class A, class B>
pair(A, B) -> pair<unwrap_ref_decay_t<A>, unwrap_ref_decay_t<B>>;
} // namespace tuplet
// tuplet::convert implementation
namespace tuplet {
// Converts from one tuple type to any other tuple or U
template <class Tuple>
struct convert {
using base_list = typename std::decay_t<Tuple>::base_list;
Tuple tuple;
template <class U>
constexpr operator U() && {
return convert_impl<U>(base_list{});
}
private:
template <class U, class... Bases>
constexpr U convert_impl(type_list<Bases...>) {
return U{static_cast<Tuple&&>(tuple).identity_t<Bases>::value...};
}
};
template <class Tuple>
convert(Tuple&) -> convert<Tuple&>;
template <class Tuple>
convert(Tuple const&) -> convert<Tuple const&>;
template <class Tuple>
convert(Tuple&&) -> convert<Tuple>;
} // namespace tuplet
// tuplet::get implementation
// tuplet::tie implementation
// tuplet::apply implementation
namespace tuplet {
template <size_t I, indexable Tup>
constexpr decltype(auto) get(Tup&& tup) {
return static_cast<Tup&&>(tup)[tag<I>()];
}
template <class... T>
constexpr tuple<T&...> tie(T&... t) {
return {t...};
}
template <class F, base_list_tuple Tup>
constexpr decltype(auto) apply(F&& func, Tup&& tup) {
return detail::apply_impl(static_cast<F&&>(func), static_cast<Tup&&>(tup),
typename std::decay_t<Tup>::base_list());
}
template <class F, class A, class B>
constexpr decltype(auto) apply(F&& func, tuplet::pair<A, B>& pair) {
return static_cast<F&&>(func)(pair.first, pair.second);
}
template <class F, class A, class B>
constexpr decltype(auto) apply(F&& func, tuplet::pair<A, B> const& pair) {
return static_cast<F&&>(func)(pair.first, pair.second);
}
template <class F, class A, class B>
constexpr decltype(auto) apply(F&& func, tuplet::pair<A, B>&& pair) {
return static_cast<F&&>(func)(std::move(pair).first, std::move(pair).second);
}
} // namespace tuplet
// tuplet::tuple_cat implementation
// tuplet::make_tuple implementation
// tuplet::forward_as_tuple implementation
namespace tuplet {
template <base_list_tuple... T>
constexpr auto tuple_cat(T&&... ts) {
if constexpr (sizeof...(T) == 0) {
return tuple<>();
}
else {
/**
* It appears that Clang produces better assembly when
* TUPLET_CAT_BY_FORWARDING_TUPLE == 0, while GCC produces better assembly when
* TUPLET_CAT_BY_FORWARDING_TUPLE == 1. MSVC always produces terrible assembly
* in either case. This will set TUPLET_CAT_BY_FORWARDING_TUPLE to the correct
* value (0 for clang, 1 for everyone else)
*
* See: https://github.com/codeinred/tuplet/discussions/14
*/
#if !defined(TUPLET_CAT_BY_FORWARDING_TUPLE)
#if defined(__clang__)
#define TUPLET_CAT_BY_FORWARDING_TUPLE 0
#else
#define TUPLET_CAT_BY_FORWARDING_TUPLE 1
#endif
#endif
#if TUPLET_CAT_BY_FORWARDING_TUPLE
using big_tuple = tuple<T&&...>;
#else
using big_tuple = tuple<std::decay_t<T>...>;
#endif
using outer_bases = base_list_t<big_tuple>;
constexpr auto outer = detail::get_outer_bases(outer_bases{});
constexpr auto inner = detail::get_inner_bases(outer_bases{});
return detail::cat_impl(big_tuple{static_cast<T&&>(ts)...}, outer, inner);
}
}
template <typename... Ts>
constexpr auto make_tuple(Ts&&... args) {
return tuple<unwrap_ref_decay_t<Ts>...>{static_cast<Ts&&>(args)...};
}
template <typename... T>
constexpr auto forward_as_tuple(T&&... a) noexcept {
return tuple<T&&...>{static_cast<T&&>(a)...};
}
} // namespace tuplet
// tuplet literals
namespace tuplet::literals {
template <char... D>
constexpr auto operator""_tag() noexcept
-> tag<detail::size_t_from_digits<D...>()> {
return {};
}
} // namespace tuplet::literals
// std::tuple_size specialization
// std::tuple_element specialization
namespace std {
template <class... T>
struct tuple_size<tuplet::tuple<T...>>
: std::integral_constant<size_t, sizeof...(T)> {};
template <size_t I, class... T>
struct tuple_element<I, tuplet::tuple<T...>> {
using type = decltype(tuplet::tuple<T...>::decl_elem(tuplet::tag<I>()));
};
template <class A, class B>
struct tuple_size<tuplet::pair<A, B>> : std::integral_constant<size_t, 2> {};
template <size_t I, class A, class B>
struct tuple_element<I, tuplet::pair<A, B>> {
static_assert(I < 2, "tuplet::pair only has 2 elements");
using type = std::conditional_t<I == 0, A, B>;
};
} // namespace std
namespace tuplet {
template <class T>
constexpr size_t tuple_size_v = std::tuple_size<T>::value;
template <size_t I, class T>
using tuple_element_t = typename std::tuple_element<I, T>::type;
} // namespace tuplet
#endif