#ifndef RFL_TUPLE_HPP_ #define RFL_TUPLE_HPP_ #include #include #include #include #include #include "internal/nth_element_t.hpp" #include "internal/ptr_cast.hpp" #include "internal/tuple/calculate_positions.hpp" namespace rfl { template class Tuple; template <> class Tuple<> { public: Tuple() {} }; template class Tuple { static constexpr size_t size_ = sizeof...(Types); static constexpr auto positions_ = internal::tuple::calculate_positions(); static constexpr auto seq_ = std::make_integer_sequence{}; static constexpr unsigned int num_bytes_ = std::get(positions_); using DataType = std::array; public: Tuple(const Types&... _t) { copy_from_types(_t..., seq_); } Tuple(Types&&... _t) noexcept { move_from_types(std::move(_t)..., seq_); } Tuple() : Tuple(Types()...) {} Tuple(const Tuple& _other) { copy_from_other(_other, seq_); } Tuple(Tuple&& _other) noexcept { move_from_other(std::move(_other), seq_); } ~Tuple() { destroy_if_necessary(seq_); } /// Gets an element by index. template constexpr auto& get() { using Type = internal::nth_element_t<_index, Types...>; return *internal::ptr_cast(data_.data() + pos<_index>()); } /// Gets an element by index. template constexpr const auto& get() const { using Type = internal::nth_element_t<_index, Types...>; return *internal::ptr_cast(data_.data() + pos<_index>()); } /// Assigns the underlying object. Tuple& operator=(const Tuple& _other) { if (this == &_other) { return *this; } auto temp = Tuple(_other); destroy_if_necessary(seq_); move_from_other(std::move(temp), seq_); return *this; } /// Assigns the underlying object. Tuple& operator=(Tuple&& _other) noexcept { if (this == &_other) { return *this; } destroy_if_necessary(seq_); move_from_other(std::move(_other), seq_); return *this; } /// Equality operator. template bool operator==(const Tuple& _other) const noexcept { static_assert(sizeof...(Types) == sizeof...(OtherTypes), "The size of the two tuples must be the same."); const auto is_same = [&](std::integral_constant) -> bool { return this->get<_i>() == _other.template get<_i>(); }; return [&](std::integer_sequence) { return (true && ... && is_same(std::integral_constant{})); }(std::make_integer_sequence()); } /// Three-way comparison operator. template auto operator<=>(const Tuple& _other) const noexcept { static_assert(sizeof...(Types) == sizeof...(OtherTypes), "The size of the two tuples must be the same."); const auto compare = [&](std::strong_ordering* _ordering, std::integral_constant) { if (*_ordering != std::strong_ordering::equivalent && this->get<_i>() != _other.template get<_i>()) { *_ordering = (this->get<_i>() <=> _other.template get<_i>()); } }; return [&](std::integer_sequence) { auto ordering = std::strong_ordering::equivalent; (compare(&ordering, std::integral_constant{}), ...); return ordering; }(std::make_integer_sequence()); } private: template void copy_from_other(const Tuple& _other, std::integer_sequence) { const auto copy_one = [this](const auto& _other, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; ::new (static_cast(data_.data() + pos<_i>())) Type(_other.template get<_i>()); }; (copy_one(_other, std::integral_constant{}), ...); } template void copy_from_types(const Types&... _types, std::integer_sequence) { const auto copy_one = [this](const auto& _t, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; ::new (static_cast(data_.data() + pos<_i>())) Type(_t); }; (copy_one(_types, std::integral_constant{}), ...); } template void destroy_if_necessary(std::integer_sequence) { const auto destroy_one = [](auto& _t) { using Type = std::remove_cvref_t; if constexpr (std::is_destructible_v) { _t.~Type(); } }; (destroy_one(get<_is>()), ...); } template void move_from_other(Tuple&& _other, std::integer_sequence) { const auto move_one = [this](auto&& _other, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; ::new (static_cast(data_.data() + pos<_i>())) Type(std::move(_other.template get<_i>())); }; (move_one(_other, std::integral_constant{}), ...); } template void move_from_types(Types&&... _types, std::integer_sequence) { const auto move_one = [this](auto&& _t, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; ::new (static_cast(data_.data() + pos<_i>())) Type(std::move(_t)); }; (move_one(std::move(_types), std::integral_constant{}), ...); } template static consteval unsigned int pos() { return std::get<_i>(positions_); } private: /// The underlying data, can be any of the underlying types. alignas(Types...) DataType data_; }; /// Gets an element by index. template constexpr auto& get(rfl::Tuple& _tup) { return _tup.template get<_index>(); } /// Gets an element by index. template constexpr const auto& get(const rfl::Tuple& _tup) { return _tup.template get<_index>(); } /// Gets an element by index. template constexpr auto& get(std::tuple& _tup) { return std::get<_index>(_tup); } /// Gets an element by index. template constexpr const auto& get(const std::tuple& _tup) { return std::get<_index>(_tup); } template auto make_tuple(Types&&... _args) { return rfl::Tuple...>(std::forward(_args)...); } template struct tuple_element; template struct tuple_element> { using type = internal::nth_element_t; }; template struct tuple_element> { using type = internal::nth_element_t; }; template using tuple_element_t = typename rfl::tuple_element>::type; template struct tuple_size; template struct tuple_size> { static constexpr auto value = sizeof...(Ts); }; template struct tuple_size> { static constexpr auto value = sizeof...(Ts); }; template inline constexpr auto tuple_size_v = rfl::tuple_size>::value; } // namespace rfl namespace std { /// Gets an element by index. template constexpr auto& get(rfl::Tuple& _tup) { return _tup.template get<_index>(); } /// Gets an element by index. template constexpr const auto& get(const rfl::Tuple& _tup) { return _tup.template get<_index>(); } } // namespace std #endif