Skip to content

Commit

Permalink
Implement P2988 optional<T&>
Browse files Browse the repository at this point in the history
  • Loading branch information
miscco committed Jan 31, 2025
1 parent cc73f98 commit 21adb81
Show file tree
Hide file tree
Showing 43 changed files with 1,213 additions and 370 deletions.
222 changes: 217 additions & 5 deletions libcudacxx/include/cuda/std/detail/libcxx/include/optional
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ template<class T>
#include <cuda/std/__functional/unary_function.h>
#include <cuda/std/__memory/addressof.h>
#include <cuda/std/__memory/construct_at.h>
#include <cuda/std/__memory/pointer_traits.h>
#include <cuda/std/__new_>
#include <cuda/std/__tuple_dir/sfinae_helpers.h>
#include <cuda/std/__type_traits/conjunction.h>
Expand All @@ -186,6 +187,8 @@ template<class T>
#include <cuda/std/__type_traits/is_move_assignable.h>
#include <cuda/std/__type_traits/is_move_constructible.h>
#include <cuda/std/__type_traits/is_object.h>
#include <cuda/std/__type_traits/is_reference.h>
#include <cuda/std/__type_traits/is_same.h>
#include <cuda/std/__type_traits/is_trivially_copy_assignable.h>
#include <cuda/std/__type_traits/is_trivially_copy_constructible.h>
#include <cuda/std/__type_traits/is_trivially_destructible.h>
Expand Down Expand Up @@ -1399,26 +1402,235 @@ operator>=(const _Tp& __v, const optional<_Up>& __x)

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI constexpr enable_if_t<
_CCCL_TRAIT(is_move_constructible, _Tp) && _CCCL_TRAIT(is_swappable, _Tp),
_CCCL_TRAIT(is_reference, _Tp) || (_CCCL_TRAIT(is_move_constructible, _Tp) && _CCCL_TRAIT(is_swappable, _Tp)),
void>
swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y)))
{
__x.swap(__y);
}

template <class _Tp>
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v)
class optional<_Tp&>
{
return optional<decay_t<_Tp>>(_CUDA_VSTD::forward<_Tp>(__v));
private:
using __raw_type = remove_reference_t<_Tp>;
__raw_type* __value_ = nullptr;

_CCCL_TEMPLATE(class _Ref, class _Arg)
_CCCL_REQUIRES(_CCCL_TRAIT(is_constructible, _Ref, _Arg))
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI static constexpr _Ref __make_reference(_Arg&& __arg) noexcept
{
static_assert(_CCCL_TRAIT(is_reference, _Ref));
return _Ref(_CUDA_VSTD::forward<_Arg>(__arg));
}

public:
using value_type = __raw_type&;

_LIBCUDACXX_HIDE_FROM_ABI constexpr optional() noexcept {}
_CCCL_HIDE_FROM_ABI constexpr optional(const optional&) noexcept = default;
_CCCL_HIDE_FROM_ABI constexpr optional(optional&&) noexcept = default;
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}

_CCCL_TEMPLATE(class _Arg)
_CCCL_REQUIRES(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Arg))
_LIBCUDACXX_HIDE_FROM_ABI explicit constexpr optional(in_place_t, _Arg&& __arg) noexcept
: __value_(_CUDA_VSTD::addressof(__make_reference<add_lvalue_reference_t<_Tp>>(_CUDA_VSTD::forward<_Arg>(__arg))))
{}

_CCCL_TEMPLATE(class _Up = _Tp)
_CCCL_REQUIRES((!__is_std_optional<decay_t<_Up>>::value) _CCCL_AND(_CCCL_TRAIT(is_convertible, _Up, _Tp)))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional(_Up&& __u) noexcept
: __value_(_CUDA_VSTD::addressof(__u))
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
static_assert(_CCCL_TRAIT(is_lvalue_reference, _Up), "U must be an lvalue");
}

_CCCL_TEMPLATE(class _Up = _Tp)
_CCCL_REQUIRES((!__is_std_optional<decay_t<_Up>>::value) _CCCL_AND((!_CCCL_TRAIT(is_convertible, _Up, _Tp))))
_LIBCUDACXX_HIDE_FROM_ABI explicit constexpr optional(_Up&& __u) noexcept
: __value_(_CUDA_VSTD::addressof(__u))
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
static_assert(_CCCL_TRAIT(is_lvalue_reference, _Up), "U must be an lvalue");
}

_CCCL_TEMPLATE(class _Up)
_CCCL_REQUIRES(_CCCL_TRAIT(is_convertible, _Up, _Tp))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional(const optional<_Up>& __u) noexcept
: __value_(__u.has_value() ? _CUDA_VSTD::to_address(__u) : nullptr)
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
}

_CCCL_TEMPLATE(class _Up)
_CCCL_REQUIRES((!_CCCL_TRAIT(is_convertible, _Up, _Tp)))
_LIBCUDACXX_HIDE_FROM_ABI explicit constexpr optional(const optional<_Up>& __u) noexcept
: __value_(__u.has_value() ? _CUDA_VSTD::to_address(__u) : nullptr)
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
}

_CCCL_HIDE_FROM_ABI constexpr optional& operator=(const optional&) noexcept = default;
_CCCL_HIDE_FROM_ABI constexpr optional& operator=(optional&&) noexcept = default;

_LIBCUDACXX_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept
{
__value_ = nullptr;
return *this;
}

_CCCL_TEMPLATE(class _Up = _Tp)
_CCCL_REQUIRES((!__is_std_optional<decay_t<_Up>>::value))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional& operator=(_Up&& __u) noexcept
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
static_assert(_CCCL_TRAIT(is_lvalue_reference, _Up), "U must be an lvalue");
__value_ = _CUDA_VSTD::addressof(__u);
return *this;
}

template <class _Up>
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional& operator=(const optional<_Up>& __u) noexcept
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
__value_ = __u.has_value() ? _CUDA_VSTD::to_address(__u) : nullptr;
return *this;
}

template <class _Up>
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional& operator=(optional<_Up>&& __u) noexcept
{
static_assert(_CCCL_TRAIT(is_constructible, add_lvalue_reference_t<_Tp>, _Up), "Must be able to bind U to T&");
__value_ = __u.has_value() ? _CUDA_VSTD::to_address(__u) : nullptr;
return *this;
}

_CCCL_TEMPLATE(class _Up = _Tp)
_CCCL_REQUIRES((!__is_std_optional<decay_t<_Up>>::value))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional& emplace(_Up&& __u) noexcept
{
return *this = _CUDA_VSTD::forward<_Up>(__u);
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr void swap(optional& __rhs) noexcept
{
return _CUDA_VSTD::swap(__value_, __rhs.__value_);
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp* operator->() const noexcept
{
_CCCL_ASSERT(__value_ != nullptr, "optional operator-> called on a disengaged value");
return __value_;
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept
{
_CCCL_ASSERT(__value_ != nullptr, "optional operator* called on a disengaged value");
return *__value_;
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept
{
return __value_ != nullptr;
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr bool has_value() const noexcept
{
return __value_ != nullptr;
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp& value() const noexcept
{
if (__value_ != nullptr)
{
return *__value_;
}
else
{
__throw_bad_optional_access();
}
}

template <class _Up>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const
{
static_assert(_CCCL_TRAIT(is_copy_constructible, _Tp), "optional<T>::value_or: T must be copy constructible");
static_assert(_CCCL_TRAIT(is_convertible, _Up, _Tp), "optional<T>::value_or: U must be convertible to T");
return __value_ != nullptr ? *__value_ : static_cast<_Tp>(_CUDA_VSTD::forward<_Up>(__v));
}

template <class _Func>
_LIBCUDACXX_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const
{
using _Up = invoke_result_t<_Func, _Tp&>;
static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
"Result of f(value()) must be a specialization of std::optional");
if (__value_ != nullptr)
{
return _CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Func>(__f), *__value_);
}
return remove_cvref_t<_Up>();
}

template <class _Func>
_LIBCUDACXX_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const
{
using _Up = invoke_result_t<_Func, _Tp&>;
static_assert(!_CCCL_TRAIT(is_array, _Up), "Result of f(value()) should not be an Array");
static_assert(!_CCCL_TRAIT(is_same, _Up, in_place_t), "Result of f(value()) should not be std::in_place_t");
static_assert(!_CCCL_TRAIT(is_same, _Up, nullopt_t), "Result of f(value()) should not be std::nullopt_t");
if (__value_ != nullptr)
{
if constexpr (_CCCL_TRAIT(is_lvalue_reference, _Up))
{
return optional<_Up>(_CUDA_VSTD::invoke(_CUDA_VSTD::forward<_Func>(__f), *__value_));
}
else
{
return optional<_Up>(__optional_construct_from_invoke_tag{}, _CUDA_VSTD::forward<_Func>(__f), *__value_);
}
}
return optional<_Up>();
}

_CCCL_TEMPLATE(class _Func)
_CCCL_REQUIRES(invocable<_Func>)
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const
{
using _Up = invoke_result_t<_Func>;
static_assert(_CCCL_TRAIT(is_same, remove_cvref_t<_Up>, optional),
"Result of f() should be the same type as this optional");
if (__value_ != nullptr)
{
return *this;
}
return _CUDA_VSTD::forward<_Func>(__f)();
}

_LIBCUDACXX_HIDE_FROM_ABI constexpr void reset() noexcept
{
__value_ = nullptr;
}
};

_CCCL_TEMPLATE(class _Tp = nullopt_t::__secret_tag, class _Up)
_CCCL_REQUIRES(_CCCL_TRAIT(is_same, _Tp, nullopt_t::__secret_tag))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional<decay_t<_Up>> make_optional(_Up&& __v)
{
return optional<decay_t<_Up>>(_CUDA_VSTD::forward<_Up>(__v));
}

template <class _Tp, class... _Args>
_CCCL_TEMPLATE(class _Tp, class... _Args)
_CCCL_REQUIRES((!_CCCL_TRAIT(is_reference, _Tp)))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args)
{
return optional<_Tp>(in_place, _CUDA_VSTD::forward<_Args>(__args)...);
}

template <class _Tp, class _Up, class... _Args>
_CCCL_TEMPLATE(class _Tp, class _Up, class... _Args)
_CCCL_REQUIRES((!_CCCL_TRAIT(is_reference, _Tp)))
_LIBCUDACXX_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(initializer_list<_Up> __il, _Args&&... __args)
{
return optional<_Tp>(in_place, __il, _CUDA_VSTD::forward<_Args>(__args)...);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,41 +34,50 @@ __host__ __device__ constexpr bool operator==(const X& lhs, const X& rhs)
return lhs.i_ == rhs.i_;
}

__host__ __device__ constexpr bool test()
template <class T>
__host__ __device__ constexpr void test()
{
{
typedef X T;
typedef optional<T> O;
using O = optional<X>;

constexpr T val(2);
X val(2);
O o1; // disengaged
O o2{1}; // engaged
O o3{val}; // engaged

assert(!(o1 == T(1)));
assert((o2 == T(1)));
assert(!(o3 == T(1)));
assert((o3 == T(2)));
assert(!(o1 == X(1)));
assert((o2 == X(1)));
assert(!(o3 == X(1)));
assert((o3 == X(2)));
assert((o3 == val));

assert(!(T(1) == o1));
assert((T(1) == o2));
assert(!(T(1) == o3));
assert((T(2) == o3));
assert(!(X(1) == o1));
assert((X(1) == o2));
assert(!(X(1) == o3));
assert((X(2) == o3));
assert((val == o3));
}

cuda::std::remove_reference_t<T> val1{42};
cuda::std::remove_reference_t<T> val2{101};
{
using O = optional<int>;
O o1(42);
assert(o1 == 42l);
assert(!(101l == o1));
using O = optional<T>;
O o1(val1);
assert(o1 == val1);
assert(!(val2 == o1));
}
{
using O = optional<const int>;
O o1(42);
assert(o1 == 42);
assert(!(101 == o1));
using O = optional<const T>;
O o1(val1);
assert(o1 == val1);
assert(!(val2 == o1));
}
}

__host__ __device__ constexpr bool test()
{
test<int>();
test<int&>();

return true;
}
Expand Down
Loading

0 comments on commit 21adb81

Please sign in to comment.