Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 9 additions & 36 deletions include/iris/compare.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,11 @@ namespace iris {
// Utilities defined in [library]
// https://eel.is/c++draft/library

namespace detail {

template<class T, class U>
struct synth_three_way_result_impl
{
using type = std::weak_ordering;
};

template<class T, class U> requires std::three_way_comparable<T, U>
struct synth_three_way_result_impl<T, U>
{
using type = std::invoke_result_t<std::compare_three_way, T const&, U const&>;
};

} // detail

template<class T, class U = T>
using synth_three_way_result_t = detail::synth_three_way_result_impl<T, U>::type;

template<class T, class U = T>
inline constexpr bool synth_three_way_noexcept =
std::conditional_t<
std::three_way_comparable_with<T, U>,
std::is_nothrow_invocable<std::compare_three_way, T const&, U const&>,
std::conjunction<
std::is_nothrow_invocable<std::less<>, T const&, U const&>,
std::is_nothrow_invocable<std::less<>, U const&, T const&>
>
>::value;

constexpr auto synth_three_way = []<class T, class U>(T const& t, U const& u) noexcept(synth_three_way_noexcept<T, U>)
-> synth_three_way_result_t<T, U>
requires requires {
{ t < u } -> req::boolean_testable;
{ u < t } -> req::boolean_testable;
}
constexpr auto synth_three_way = []<class T, class U>(T const& t, U const& u)
//requires requires {
// { t < u } -> req::boolean_testable;
// { u < t } -> req::boolean_testable;
//}
{
if constexpr (std::three_way_comparable_with<T, U>) {
return t <=> u;
Expand All @@ -60,6 +29,10 @@ constexpr auto synth_three_way = []<class T, class U>(T const& t, U const& u) no
}
};

template<class T, class U = T>
using synth_three_way_result_t = decltype(synth_three_way(std::declval<T&>(), std::declval<U&>()));


namespace cmp {

template<class Compare, class T>
Expand Down
80 changes: 50 additions & 30 deletions include/iris/indirect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,10 @@ class scoped_allocation
pointer ptr_;
};

} // detail


// Polyfill for the C++26 `std::indirect`
// https://eel.is/c++draft/indirect
template<class T, class Allocator = std::allocator<T>>
class indirect
class indirect_base
{
static_assert(std::is_object_v<T>);
static_assert(!std::is_array_v<T>);
Expand All @@ -72,36 +69,36 @@ class indirect
using pointer = std::allocator_traits<Allocator>::pointer;
using const_pointer = std::allocator_traits<Allocator>::const_pointer;

constexpr explicit indirect() requires std::is_default_constructible_v<Allocator>
constexpr explicit indirect_base() requires std::is_default_constructible_v<Allocator>
: ptr_(make_obj())
{}

constexpr indirect(indirect const& other)
: indirect(
constexpr indirect_base(indirect_base const& other)
: indirect_base(
std::allocator_arg,
std::allocator_traits<Allocator>::select_on_container_copy_construction(other.alloc_),
other
)
{}

constexpr explicit indirect(std::allocator_arg_t, Allocator const& a)
constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a)
: alloc_(a)
, ptr_(make_obj())
{}

constexpr indirect(std::allocator_arg_t, Allocator const& a, indirect const& other)
constexpr indirect_base(std::allocator_arg_t, Allocator const& a, indirect_base const& other)
: alloc_(a)
, ptr_(other.ptr_ ? make_obj(std::as_const(*other.ptr_)) : nullptr)
{
static_assert(std::is_copy_constructible_v<T>);
}

constexpr indirect(indirect&& other) noexcept
constexpr indirect_base(indirect_base&& other) noexcept
: alloc_(std::move(other.alloc_))
, ptr_(std::exchange(other.ptr_, nullptr))
{}

constexpr indirect(std::allocator_arg_t, Allocator const& a, indirect&& other)
constexpr indirect_base(std::allocator_arg_t, Allocator const& a, indirect_base&& other)
noexcept(std::allocator_traits<Allocator>::is_always_equal::value)
: alloc_(a)
, ptr_(alloc_ == other.alloc_
Expand All @@ -112,20 +109,20 @@ class indirect

template<class U = T>
requires
(!std::is_same_v<std::remove_cvref_t<U>, indirect>) &&
(!std::is_same_v<std::remove_cvref_t<U>, indirect_base>) &&
(!std::is_same_v<std::remove_cvref_t<U>, std::in_place_t>) &&
std::is_constructible_v<T, U> &&
std::is_default_constructible_v<Allocator>
constexpr explicit indirect(U&& u)
constexpr explicit indirect_base(U&& u)
: ptr_(make_obj(std::forward<U>(u)))
{}

template<class U = T>
requires
(!std::is_same_v<std::remove_cvref_t<U>, indirect>) &&
(!std::is_same_v<std::remove_cvref_t<U>, indirect_base>) &&
(!std::is_same_v<std::remove_cvref_t<U>, std::in_place_t>) &&
std::is_constructible_v<T, U>
constexpr explicit indirect(std::allocator_arg_t, Allocator const& a, U&& u)
constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, U&& u)
: alloc_(a)
, ptr_(make_obj(std::forward<U>(u)))
{}
Expand All @@ -134,14 +131,14 @@ class indirect
requires
std::is_constructible_v<T, Us...> &&
std::is_default_constructible_v<Allocator>
constexpr explicit indirect(std::in_place_t, Us&&... us)
constexpr explicit indirect_base(std::in_place_t, Us&&... us)
: ptr_(make_obj(std::forward<Us>(us)...))
{}

template<class... Us>
requires
std::is_constructible_v<T, Us...>
constexpr explicit indirect(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us)
constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, std::in_place_t, Us&&... us)
: alloc_(a)
, ptr_(make_obj(std::forward<Us>(us)...))
{}
Expand All @@ -150,26 +147,26 @@ class indirect
requires
std::is_constructible_v<T, std::initializer_list<I>&, Us...> &&
std::is_default_constructible_v<Allocator>
constexpr explicit indirect(std::in_place_t, std::initializer_list<I> il, Us&&... us)
constexpr explicit indirect_base(std::in_place_t, std::initializer_list<I> il, Us&&... us)
: ptr_(make_obj(il, std::forward<Us>(us)...))
{}

template<class I, class... Us>
requires
std::is_constructible_v<T, std::initializer_list<I>&, Us...>
constexpr explicit indirect(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list<I> il, Us&&... us)
constexpr explicit indirect_base(std::allocator_arg_t, Allocator const& a, std::in_place_t, std::initializer_list<I> il, Us&&... us)
: alloc_(a)
, ptr_(make_obj(il, std::forward<Us>(us)...))
{}

constexpr ~indirect() noexcept
constexpr ~indirect_base() noexcept
{
if (ptr_) [[likely]] {
destroy_deallocate();
}
}

constexpr indirect& operator=(indirect const& other)
constexpr indirect_base& operator=(indirect_base const& other)
{
static_assert(std::is_copy_assignable_v<T>);
static_assert(std::is_copy_constructible_v<T>);
Expand Down Expand Up @@ -247,7 +244,7 @@ class indirect
}
}

constexpr indirect& operator=(indirect&& other)
constexpr indirect_base& operator=(indirect_base&& other)
noexcept(
std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
std::allocator_traits<Allocator>::is_always_equal::value
Expand Down Expand Up @@ -308,10 +305,10 @@ class indirect

template<class U = T>
requires
(!std::is_same_v<std::remove_cvref_t<U>, indirect>) &&
(!std::is_same_v<std::remove_cvref_t<U>, indirect_base>) &&
std::is_constructible_v<T, U> &&
std::is_assignable_v<T&, U>
constexpr indirect& operator=(U&& u)
constexpr indirect_base& operator=(U&& u)
{
if (ptr_) [[likely]] {
**this = std::forward<U>(u);
Expand All @@ -334,7 +331,7 @@ class indirect

[[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return alloc_; }

constexpr void swap(indirect& other)
constexpr void swap(indirect_base& other)
noexcept(
std::allocator_traits<Allocator>::propagate_on_container_swap::value ||
std::allocator_traits<Allocator>::is_always_equal::value
Expand All @@ -348,7 +345,7 @@ class indirect
}
}

friend constexpr void swap(indirect& lhs, indirect& rhs) noexcept(noexcept(lhs.swap(rhs)))
friend constexpr void swap(indirect_base& lhs, indirect_base& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
return lhs.swap(rhs);
}
Expand All @@ -372,6 +369,17 @@ class indirect
pointer ptr_;
};

} // detail

// Polyfill for the C++26 `std::indirect`
// https://eel.is/c++draft/indirect
template<class T, class Allocator = std::allocator<T>>
class indirect : public detail::indirect_base<T, Allocator>
{
public:
using detail::indirect_base<T, Allocator>::indirect_base;
};

template<class Value>
indirect(Value)
-> indirect<Value>;
Expand All @@ -394,7 +402,7 @@ constexpr bool operator==(indirect<T, Allocator> const& lhs, indirect<U, AA> con

template<class T, class Allocator, class U, class AA>
constexpr auto operator<=>(indirect<T, Allocator> const& lhs, indirect<U, AA> const& rhs)
noexcept(synth_three_way_noexcept<T, U>) -> synth_three_way_result_t<T, U>
-> synth_three_way_result_t<T, U>
{
if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] {
return !lhs.valueless_after_move() <=> !rhs.valueless_after_move();
Expand All @@ -414,9 +422,12 @@ constexpr bool operator==(indirect<T, Allocator> const& lhs, U const& rhs)
}
}

template<class T, class Allocator, class U> requires (!is_ttp_specialization_of_v<U, indirect>)
constexpr auto operator<=>(indirect<T, Allocator> const& lhs, U const& rhs)
noexcept(synth_three_way_noexcept<T, U>) -> synth_three_way_result_t<T, U>

namespace detail {

template<class T, class A, class U>
constexpr auto three_way_compare_impl(indirect<T, A> const& lhs, U const& rhs)
-> synth_three_way_result_t<T, U>
{
if (lhs.valueless_after_move()) [[unlikely]] {
return std::strong_ordering::less;
Expand All @@ -425,6 +436,15 @@ constexpr auto operator<=>(indirect<T, Allocator> const& lhs, U const& rhs)
}
}

} // detail

template<class T, class A, class U>
constexpr auto operator<=>(indirect<T, A> const& lhs, U const& rhs)
// no explicit return type
{
return detail::three_way_compare_impl(lhs, rhs);
}

} // iris


Expand Down
20 changes: 16 additions & 4 deletions include/iris/rvariant/recursive_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace iris {

template<class T, class Allocator = std::allocator<T>>
class recursive_wrapper
: private iris::indirect<T, Allocator>
: private iris::detail::indirect_base<T, Allocator>
{
static_assert(std::is_object_v<T>);
static_assert(!std::is_array_v<T>);
Expand All @@ -25,7 +25,7 @@ class recursive_wrapper
static_assert(!std::is_const_v<T> && !std::is_volatile_v<T>);
static_assert(std::is_same_v<T, typename std::allocator_traits<Allocator>::value_type>);

using base_type = iris::indirect<T, Allocator>;
using base_type = iris::detail::indirect_base<T, Allocator>;

public:
using typename base_type::allocator_type;
Expand Down Expand Up @@ -178,7 +178,7 @@ constexpr bool operator==(recursive_wrapper<T, TA> const& lhs, recursive_wrapper
}

template<class T, class TA, class U, class UA>
constexpr auto operator<=>(recursive_wrapper<T, TA> const& lhs, recursive_wrapper<U, UA> const& rhs) noexcept(synth_three_way_noexcept<T, U>)
constexpr auto operator<=>(recursive_wrapper<T, TA> const& lhs, recursive_wrapper<U, UA> const& rhs)
-> synth_three_way_result_t<T, U>
{
if (lhs.valueless_after_move() || rhs.valueless_after_move()) [[unlikely]] {
Expand All @@ -199,8 +199,11 @@ constexpr bool operator==(recursive_wrapper<T, A> const& lhs, U const& rhs)
}
}

namespace detail {

template<class T, class A, class U>
constexpr auto operator<=>(recursive_wrapper<T, A> const& lhs, U const& rhs) noexcept(synth_three_way_noexcept<T, U>) -> synth_three_way_result_t<T, U>
constexpr auto three_way_compare_impl(recursive_wrapper<T, A> const& lhs, U const& rhs)
-> synth_three_way_result_t<T, U>
{
if (lhs.valueless_after_move()) [[unlikely]] {
return std::strong_ordering::less;
Expand All @@ -209,6 +212,15 @@ constexpr auto operator<=>(recursive_wrapper<T, A> const& lhs, U const& rhs) noe
}
}

} // detail

template<class T, class A, class U>
constexpr auto operator<=>(recursive_wrapper<T, A> const& lhs, U const& rhs)
// no explicit return type
{
return detail::three_way_compare_impl(lhs, rhs);
}

} // iris

namespace std {
Expand Down
Loading
Loading