Skip to content

Commit

Permalink
Big tuple<> refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
vector-of-bool committed Apr 2, 2024
1 parent 0bcab9b commit 67fba1e
Show file tree
Hide file tree
Showing 5 changed files with 553 additions and 298 deletions.
117 changes: 117 additions & 0 deletions src/neo/core/tuple.detail.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include <neo/attrib.hpp>
#include <neo/compare.hpp>
#include <neo/get.hpp>
#include <neo/object_t.hpp>
#include <neo/type_traits.hpp>

#include <compare>
#include <tuple>
#include <utility>

namespace neo::core::_tuple_detail {

/// An indexed box to hold tuple elements
template <std::size_t, typename T>
struct nth_box {
// The value for this element
NEO_NO_UNIQUE_ADDRESS object_t<T> _obox;

using lref = add_lvalue_reference_t<nonvoid_t<T>>;
using cref = add_const_reference_t<nonvoid_t<T>>;

constexpr nth_box() = default;
constexpr explicit nth_box(std::in_place_t, auto&& t)
: _obox(NEO_FWD(t)) {}

bool operator==(nth_box const&) const = default;
auto operator<=>(nth_box const&) const = default;

constexpr lref get() noexcept { return static_cast<lref>(_obox); }
constexpr cref get() const noexcept { return static_cast<cref>(_obox); }
};

template <typename Tpl, typename Seq = std::make_index_sequence<meta::len_v<Tpl>>>
struct boxes;

template <std::size_t... Is, neo_ttparam Tpl, typename... Ts>
struct boxes<Tpl<Ts...>, std::index_sequence<Is...>> : nth_box<Is, Ts>... {
constexpr boxes() = default;

template <typename... Args>
constexpr explicit boxes(std::in_place_t tag, Args&&... args) //
: nth_box<Is, Ts>(tag, NEO_FWD(args))... {}

bool operator==(boxes const&) const = default;
auto operator<=>(boxes const&) const = default;
};

template <std::size_t... Ns>
constexpr bool equal(const auto& left, const auto& right, std::index_sequence<Ns...>) noexcept {
return (static_cast<bool>(left.template get<Ns>() == right.template get<Ns>()) and ...);
}

template <typename T, typename U, std::size_t... Ns>
constexpr auto compare_three_way(const T& lhs, const U& rhs, std::index_sequence<Ns...>) noexcept {
using Category = std::common_comparison_category_t<
std::compare_three_way_result_t<decltype(lhs.template get<Ns>()),
decltype(rhs.template get<Ns>())>...>;
Category ret = std::strong_ordering::equal;
static_cast<void>(
((0 != (ret = synth_three_way(lhs.template get<Ns>(), rhs.template get<Ns>()))) or ...
or true));
return ret;
}

template <typename L>
requires requires { std::tuple_size<L>::value; }
using index_sequence_for_tpl = std::make_index_sequence<std::tuple_size_v<L>>;

template <typename Tpl, std::size_t... Ns>
requires(can_get_nth<Tpl, Ns> and ...)
constexpr void tuple_like_check(Tpl&& tpl, std::index_sequence<Ns...>);

template <typename Tpl>
concept tuple_like = requires(Tpl&& tpl) {
typename index_sequence_for_tpl<remove_cvref_t<Tpl>>;
tuple_like_check(static_cast<Tpl&&>(tpl), index_sequence_for_tpl<remove_cvref_t<Tpl>>{});
};

template <typename OtherTpl, typename BaseTpl>
concept matching_tuple_like = tuple_like<OtherTpl>
and std::tuple_size_v<remove_cvref_t<OtherTpl>> == std::tuple_size_v<BaseTpl>;

template <typename Tpl, typename Argument, std::size_t... Ns>
requires(
(std::constructible_from<object_t<std::tuple_element_t<Ns, Tpl>>, get_nth_t<Argument, Ns>>
and ...))
void check_tuple_constructible(std::index_sequence<Ns...>);

template <typename Tpl, typename Argument>
concept tuple_constructible = matching_tuple_like<Argument, Tpl> and requires(Argument&& from) {
check_tuple_constructible<Tpl, Argument>(index_sequence_for_tpl<Tpl>{});
};

template <typename FromTpl, typename ToTpl, std::size_t... Ns>
requires((std::convertible_to<get_nth_t<FromTpl, Ns>, object_t<std::tuple_element_t<Ns, ToTpl>>>
and ...))
void check_tuple_convertible(std::index_sequence<Ns...>);

template <typename FromTpl, typename ToTpl>
concept tuple_convertible = tuple_constructible<ToTpl, FromTpl>
and requires { check_tuple_convertible<FromTpl, ToTpl>(index_sequence_for_tpl<ToTpl>{}); };

template <typename ToTpl, typename Argument, std::size_t... Ns>
requires((nothrow_constructible_from<object_t<std::tuple_element_t<Ns, ToTpl>>,
get_nth_t<Argument, Ns>>
and ...))
void check_tuple_construct_nothrow(std::index_sequence<Ns...>);

template <typename ToTpl, typename Argument>
concept tuple_construct_nothrow
= tuple_constructible<ToTpl, Argument> and requires(Argument&& from) {
check_tuple_construct_nothrow<ToTpl, Argument>(index_sequence_for_tpl<ToTpl>{});
};

} // namespace neo::core::_tuple_detail
249 changes: 249 additions & 0 deletions src/neo/core/tuple.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#pragma once

#include "./tuple.detail.hpp"

#include <neo/attrib.hpp>
#include <neo/concepts.hpp>
#include <neo/get.hpp>
#include <neo/meta.hpp>

#include <utility>

namespace neo {

// Tag used to select the piece-wise constructor for a tuple
struct tuple_construct_tag {};
// Tag to select a tuple->tuple converting constructor
struct tuple_convert_tag {};

} // namespace neo

namespace neo::core {

/**
* @brief A core::tuple is like a full neo::tuple but provides a simpler interface
* upon which the more expressive interface is constructed.
*/
template <typename... Ts>
class tuple {
public:
using index_sequence = std::make_index_sequence<sizeof...(Ts)>;

template <std::size_t N>
using nth_type = nonvoid_t<meta::pack_at<N, Ts...>>;

private:
using boxes = _tuple_detail::boxes<tuple>;

NEO_NO_UNIQUE_ADDRESS boxes _boxes;

template <std::size_t N>
using _nth_box = _tuple_detail::nth_box<N, meta::pack_at<N, Ts...>>;

public:
tuple() = default;

/**
* @brief Tuple per-element-converting constructor.
*
* @param tag A tag to select this constructor
* @param args... Arguments that will be converted to the tuple elements.
*/
template <typename... Args>
constexpr explicit tuple(tuple_construct_tag,
Args&&... args) //
noexcept((nothrow_constructible_from<nonvoid_t<Ts>, Args> and ...))
: _boxes(std::in_place, NEO_FWD(args)...) {}

/**
* @brief From-tuple converting constructor. Converts from another tuple-like
* object (using neo::get_nth<N>) for each tuple element.
*
* @param tag The tag to select this constructor
* @param tpl The tuple-like object to construct from. Elements will be perfect-forwarded from
* this tuple
*/
template <typename Tpl, auto... N>
constexpr explicit tuple(tuple_convert_tag,
Tpl&& tpl,
std::index_sequence<N...>) //
noexcept((nothrow_constructible_from<nonvoid_t<Ts>, get_nth_t<Tpl, N>> and ...))
: _boxes(std::in_place, get_nth<N>(NEO_FWD(tpl))...) {}

template <std::size_t N>
constexpr add_lvalue_reference_t<nth_type<N>> get() & noexcept {
auto& box = static_cast<_nth_box<N>&>(_boxes);
return static_cast<add_lvalue_reference_t<nth_type<N>>>(box.get());
}

template <std::size_t N>
constexpr add_lvalue_reference_t<const nth_type<N>> get() const& noexcept {
auto& box = static_cast<const _nth_box<N>&>(_boxes);
return static_cast<add_lvalue_reference_t<const nth_type<N>>>(box.get());
}

template <std::size_t N>
constexpr add_rvalue_reference_t<nth_type<N>> get() && noexcept {
auto& box = static_cast<_nth_box<N>&>(_boxes);
return static_cast<add_rvalue_reference_t<nth_type<N>>>(box.get());
}

template <std::size_t N>
constexpr add_rvalue_reference_t<const nth_type<N>> get() const&& noexcept {
auto& box = static_cast<const _nth_box<N>&>(_boxes);
return static_cast<add_rvalue_reference_t<const nth_type<N>>>(box.get());
}

bool operator==(tuple const&) const = default;
auto operator<=>(tuple const&) const = default;
};

// Specialization of an empty tuple
template <>
class tuple<> {
public:
using index_sequence = std::index_sequence<>;

tuple() = default;
constexpr explicit tuple(tuple_construct_tag) noexcept {}
template <typename Tpl>
requires(not can_get_nth<Tpl, 0>)
constexpr explicit tuple(tuple_convert_tag, Tpl&&, std::index_sequence<>) noexcept {}

template <std::size_t>
void get() = delete;

bool operator==(tuple const&) const = default;
auto operator<=>(tuple const&) const = default;
};

// Specialization for a 1-element tuple
template <typename Single>
class tuple<Single> {
using Type = nonvoid_t<Single>;
NEO_NO_UNIQUE_ADDRESS object_t<Single> _value;

public:
using index_sequence = std::index_sequence<0>;

tuple() = default;

template <explicit_convertible_to<Type> Arg>
constexpr explicit tuple(tuple_construct_tag,
Arg&& arg) //
noexcept(nothrow_constructible_from<Type, Arg>)
: _value(NEO_FWD(arg)) {}

template <typename Tpl>
requires(can_get_nth<Tpl, 0>)
constexpr explicit tuple(tuple_convert_tag, Tpl&& tpl, std::index_sequence<0>) noexcept(
nothrow_constructible_from<Type, get_nth_t<Tpl, 0>>)
: _value(get_nth<0>(NEO_FWD(tpl))) {}

template <std::size_t N>
requires(N == 0)
constexpr add_lvalue_reference_t<Type> get() & noexcept {
return static_cast<add_lvalue_reference_t<Type>>(_value);
}

template <std::size_t N>
requires(N == 0)
constexpr add_const_reference_t<Type> get() const& noexcept {
return static_cast<add_const_reference_t<Type>>(_value);
}

template <std::size_t N>
requires(N == 0)
constexpr add_rvalue_reference_t<Type> get() && noexcept {
return static_cast<add_rvalue_reference_t<Type>>(_value);
}

template <std::size_t N>
requires(N == 0)
constexpr add_rvalue_reference_t<const Type> get() const&& noexcept {
return static_cast<add_rvalue_reference_t<const Type>>(_value);
}

bool operator==(tuple const&) const = default;
auto operator<=>(tuple const&) const = default;
};

// Specialization for a 2-element tuple
template <typename T, typename U>
class tuple<T, U> {
NEO_NO_UNIQUE_ADDRESS object_t<T> _first;
NEO_NO_UNIQUE_ADDRESS object_t<U> _second;

using Tnv = nonvoid_t<T>;
using Unv = nonvoid_t<U>;

template <std::size_t N>
using _type = conditional_t<N, Unv, Tnv>;

public:
using index_sequence = std::index_sequence<0, 1>;

tuple() = default;

template <convertible_to<Tnv> Arg1, convertible_to<Unv> Arg2>
constexpr explicit tuple(tuple_construct_tag,
Arg1&& arg1,
Arg2&& arg2) //
noexcept(nothrow_constructible_from<Tnv, Arg1> and nothrow_constructible_from<Unv, Arg2>)
: _first(NEO_FWD(arg1))
, _second(NEO_FWD(arg2)) {}

template <typename Tpl>
requires(can_get_nth<Tpl, 1>)
constexpr explicit tuple(tuple_convert_tag, Tpl&& tpl, std::index_sequence<0, 1>) noexcept(
nothrow_constructible_from<Tnv, get_nth_t<Tpl, 0>>
and nothrow_constructible_from<Unv, get_nth_t<Tpl, 1>>)
: _first(get_nth<0>(NEO_FWD(tpl)))
, _second(get_nth<1>(NEO_FWD(tpl))) {}

template <std::size_t N>
constexpr _type<N>& get() & noexcept {
if constexpr (N)
return static_cast<Unv&>(_second);
else
return static_cast<Tnv&>(_first);
}

template <std::size_t N>
constexpr _type<N> const& get() const& noexcept {
if constexpr (N)
return static_cast<Unv const&>(_second);
else
return static_cast<Tnv const&>(_first);
}

template <std::size_t N>
constexpr _type<N>&& get() && noexcept {
if constexpr (N)
return static_cast<Unv&&>(_second);
else
return static_cast<Tnv&&>(_first);
}

template <std::size_t N>
constexpr _type<N> const&& get() const&& noexcept {
if constexpr (N)
return static_cast<const Unv&&>(_second);
else
return static_cast<const Tnv&&>(_first);
}

bool operator==(tuple const&) const = default;
auto operator<=>(tuple const&) const = default;
};

} // namespace neo::core

template <typename... Ts>
struct std::tuple_size<neo::core::tuple<Ts...>> {
constexpr static inline std::size_t value = sizeof...(Ts);
};

template <std::size_t N, typename... Ts>
struct std::tuple_element<N, neo::core::tuple<Ts...>>
: neo::meta::tag<neo::meta::at<neo::core::tuple<Ts...>, N>> {};
Loading

0 comments on commit 67fba1e

Please sign in to comment.