Skip to content

Commit

Permalink
Implement C++26 reference_wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
lackhole committed Oct 17, 2024
1 parent c0003c7 commit 5301254
Show file tree
Hide file tree
Showing 4 changed files with 359 additions and 14 deletions.
349 changes: 349 additions & 0 deletions include/preview/__functional/reference_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
//
// Created by yonggyulee on 2024. 10. 14.
//

#ifndef PREVIEW_FUNCTIONAL_REFERENCE_WRAPPER_H_
#define PREVIEW_FUNCTIONAL_REFERENCE_WRAPPER_H_

#include <functional>
#include <type_traits>
#include <utility>

#include "preview/config.h"
#include "preview/__core/std_version.h"
#include "preview/__compare/synth_three_way.h"
#include "preview/__functional/is_reference_wrapper.h"
#include "preview/__memory/addressof.h"
#include "preview/__type_traits/detail/invoke.h"
#include "preview/__type_traits/basic_common_reference.h"
#include "preview/__type_traits/bool_constant.h"
#include "preview/__type_traits/conjunction.h"
#include "preview/__type_traits/common_reference.h"
#include "preview/__type_traits/is_complete.h"
#include "preview/__type_traits/is_invocable.h"
#include "preview/__type_traits/negation.h"
#include "preview/__type_traits/remove_cvref.h"
#include "preview/__utility/in_place.h"
#include "preview/__utility/cxx20_rel_ops.h"

#if PREVIEW_CONFORM_CXX20_STANDARD
#include <concepts>
#endif

namespace preview {
namespace detail {

struct can_bind_to_reference_helper {
template<typename T>
static void FUN(T&) noexcept;

#if defined(_MSC_VER) && _MSC_VER < 1920
template<typename T, std::enable_if_t<always_false<T>::value, int> = 0>
static auto FUN(T&&) -> void;
#else
template<typename T>
static void FUN(T&&) = delete;
#endif

template<typename U, typename T>
static auto test(int) -> decltype(FUN<T>(std::declval<U&>()), std::true_type{});
template<typename U, typename T>
static auto test(...) -> std::false_type;
};

template<typename U, typename T>
struct can_bind_to_reference : decltype(can_bind_to_reference_helper::test<U, T>(0)) {};
template<typename U, typename T>
struct can_bind_to_reference_noexcept : bool_constant<noexcept(can_bind_to_reference_helper::FUN<T>(std::declval<U>()))> {};

} // namespace detail

template<typename T>
class reference_wrapper {
public:
using type = T;

template<typename U, std::enable_if_t<conjunction<
detail::can_bind_to_reference<U, T>,
negation< std::is_same<remove_cvref_t<U>, preview::reference_wrapper<T>> >,
negation< std::is_same<remove_cvref_t<U>, std::reference_wrapper<T>> >
>::value, int> = 0>
constexpr reference_wrapper(U&& u)
noexcept(detail::can_bind_to_reference_noexcept<U, T>::value)
: reference_wrapper(in_place, std::forward<U>(u)) {}

constexpr reference_wrapper(const reference_wrapper& x) noexcept
: ptr_(x.ptr_) {}
constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;

constexpr reference_wrapper(std::reference_wrapper<T> x) noexcept
: reference_wrapper(in_place, x.get()) {}
constexpr reference_wrapper& operator=(std::reference_wrapper<T> x) noexcept {
ptr_ = preview::addressof(x.get());
}

constexpr operator T& () const noexcept {
return *ptr_;
}

constexpr T& get() const noexcept {
return *ptr_;
}

// Not a standard function! This is required for std::invoke to handle reference_wrapper correctly.
constexpr T& operator*() const noexcept {
return *ptr_;
}

// Not a standard function! Use this function on deduced context is needed
std::reference_wrapper<T> to_std() {
return get();
}

template<typename... ArgTypes>
constexpr invoke_result_t<T&, ArgTypes...>
operator()(ArgTypes&&... args) const noexcept(is_nothrow_invocable<T&, ArgTypes...>::value) {
static_assert(is_complete_v<T>, "T must be complete type");
return preview::detail::INVOKE(get(), std::forward<ArgTypes>(args)...);
}

template<typename U = T, std::enable_if_t<rel_ops::is_equality_comparable<U&, U&>::value, int> = 0>
friend constexpr bool operator==(reference_wrapper x, reference_wrapper y) {
return x.get() == y.get();
}
#if !PREVIEW_CONFORM_CXX20_STANDARD
template<typename U = T, std::enable_if_t<rel_ops::is_equality_comparable<U&, U&>::value, int> = 0>
friend constexpr bool operator!=(reference_wrapper x, reference_wrapper y) {
return !(x == y);
}
#endif

template<typename U = T, std::enable_if_t<rel_ops::is_equality_comparable<U&, const U&>::value, int> = 0>
friend constexpr bool operator==(reference_wrapper x, const T& y) {
return x.get() == y;
}
#if !PREVIEW_CONFORM_CXX20_STANDARD
template<typename U = T, std::enable_if_t<rel_ops::is_equality_comparable<U&, const U&>::value, int> = 0>
friend constexpr bool operator==(const T& y, reference_wrapper x) {
return x == y;
}
template<typename U = T, std::enable_if_t<rel_ops::is_equality_comparable<U&, const U&>::value, int> = 0>
friend constexpr bool operator!=(reference_wrapper x, const T& y) {
return !(x == y);
}
template<typename U = T, std::enable_if_t<rel_ops::is_equality_comparable<U&, const U&>::value, int> = 0>
friend constexpr bool operator!=(const T& y, reference_wrapper x) {
return !(x == y);
}
#endif

// T == const T
template<typename U = T, std::enable_if_t<conjunction_v<negation<std::is_const<U>>, rel_ops::is_equality_comparable<U&, const U&>>, int> = 0>
friend constexpr bool operator==(reference_wrapper x, reference_wrapper<const T> y) {
return x.get() == y.get();
}
#if !PREVIEW_CONFORM_CXX20_STANDARD
// T != const T
template<typename U = T, std::enable_if_t<conjunction_v<negation<std::is_const<U>>, rel_ops::is_equality_comparable<U&, const U&>>, int> = 0>
friend constexpr bool operator!=(reference_wrapper x, reference_wrapper<const T> y) {
return !(x == y);
}
// const T == T
template<typename U = T, std::enable_if_t<conjunction_v<std::is_const<U>, rel_ops::is_equality_comparable<std::remove_const_t<U>&, U&>>, int> = 0>
friend constexpr bool operator==(reference_wrapper x, reference_wrapper<std::remove_const_t<T>> y) {
return y.get() == x.get();
}
// const T != T
template<typename U = T, std::enable_if_t<conjunction_v<std::is_const<U>, rel_ops::is_equality_comparable<std::remove_const_t<U>&, U&>>, int> = 0>
friend constexpr bool operator!=(reference_wrapper x, reference_wrapper<std::remove_const_t<T>> y) {
return !(y == x);
}
#endif

template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, U&>::value, int> = 0>
friend constexpr bool operator<(reference_wrapper x, reference_wrapper y) {
return x.get() < y.get();
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, U&>::value, int> = 0>
friend constexpr bool operator<=(reference_wrapper x, reference_wrapper y) {
return !(y < x);
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, U&>::value, int> = 0>
friend constexpr bool operator>(reference_wrapper x, reference_wrapper y) {
return y < x;
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, U&>::value, int> = 0>
friend constexpr bool operator>=(reference_wrapper x, reference_wrapper y) {
return !(x < y);
}

template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator<(reference_wrapper x, const T& y) {
return x.get() < y;
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator<=(reference_wrapper x, const T& y) {
return !(y < x);
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator>(reference_wrapper x, const T& y) {
return y < x;
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator>=(reference_wrapper x, const T& y) {
return !(x < y);
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator<(const T& x, reference_wrapper y) {
return x < y.get();
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator<=(const T& x, reference_wrapper y) {
return !(y < x);
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator>(const T& x, reference_wrapper y) {
return y < x;
}
template<typename U = T, std::enable_if_t<detail::synth_three_way_possible<U&, const U&>::value, int> = 0>
friend constexpr bool operator>=(const T& x, reference_wrapper y) {
return !(x < y);
}

template<typename U = T, std::enable_if_t<conjunction_v<negation<std::is_const<U>>, detail::synth_three_way_possible<U&, const U&>>, int> = 0>
friend constexpr bool operator<(reference_wrapper x, reference_wrapper<const T> y) {
return x.get() < y.get();
}
template<typename U = T, std::enable_if_t<conjunction_v<negation<std::is_const<U>>, detail::synth_three_way_possible<U&, const U&>>, int> = 0>
friend constexpr bool operator<=(reference_wrapper x, reference_wrapper<const T> y) {
return !(y < x);
}
template<typename U = T, std::enable_if_t<conjunction_v<negation<std::is_const<U>>, detail::synth_three_way_possible<U&, const U&>>, int> = 0>
friend constexpr bool operator>(reference_wrapper x, reference_wrapper<const T> y) {
return y < x;
}
template<typename U = T, std::enable_if_t<conjunction_v<negation<std::is_const<U>>, detail::synth_three_way_possible<U&, const U&>>, int> = 0>
friend constexpr bool operator>=(reference_wrapper x, reference_wrapper<const T> y) {
return !(x < y);
}
template<typename U = T, std::enable_if_t<conjunction_v<std::is_const<U>, detail::synth_three_way_possible<U&, std::remove_const_t<U>&>>, int> = 0>
friend constexpr bool operator<(reference_wrapper x, reference_wrapper<std::remove_const_t<T>> y) {
return x.get() < y.get();
}
template<typename U = T, std::enable_if_t<conjunction_v<std::is_const<U>, detail::synth_three_way_possible<U&, std::remove_const_t<U>&>>, int> = 0>
friend constexpr bool operator<=(reference_wrapper x, reference_wrapper<std::remove_const_t<T>> y) {
return !(y < x);
}
template<typename U = T, std::enable_if_t<conjunction_v<std::is_const<U>, detail::synth_three_way_possible<U&, std::remove_const_t<U>&>>, int> = 0>
friend constexpr bool operator>(reference_wrapper x, reference_wrapper<std::remove_const_t<T>> y) {
return y < x;
}
template<typename U = T, std::enable_if_t<conjunction_v<std::is_const<U>, detail::synth_three_way_possible<U&, std::remove_const_t<U>&>>, int> = 0>
friend constexpr bool operator>=(reference_wrapper x, reference_wrapper<std::remove_const_t<T>> y) {
return !(x < y);
}

private:
constexpr reference_wrapper(in_place_t, T& ref)
: ptr_(preview::addressof(ref)) {}

T* ptr_;
};

#if PREVIEW_CXX_VERSION >= 17

template< typename T >
reference_wrapper( T& ) -> reference_wrapper<T>;

#endif

template<typename T>
constexpr reference_wrapper<T> ref(T& t) noexcept {
return reference_wrapper<T>(t);
}

template<typename T>
constexpr reference_wrapper<T> ref(reference_wrapper<T> t) noexcept {
return t;
}

template<typename T>
constexpr reference_wrapper<T> ref(std::reference_wrapper<T> t) noexcept {
return reference_wrapper<T>(t.get());
}


template<typename T>
constexpr reference_wrapper<const T> cref(const T& t) noexcept {
return reference_wrapper<const T>(t);
}

template<typename T>
constexpr reference_wrapper<const T> cref(reference_wrapper<T> t) noexcept {
return t;
}

template<typename T>
constexpr reference_wrapper<const T> cref(std::reference_wrapper<T> t) noexcept {
return reference_wrapper<const T>(t.get());
}

// TODO: Specialize std::basic_common_reference for preview::reference_wrapper

template<typename T>
struct is_reference_wrapper<reference_wrapper<T>> : std::true_type {};

namespace detail {

#if PREVIEW_CONFORM_CXX20_STANDARD

template<typename R, typename T, typename RQ, typename TQ>
concept ref_wrap_common_reference_exists_with =
is_specialization_v<R, preview::reference_wrapper> &&
requires { typename preview::common_reference_t<typename R::type&, TQ>; } &&
std::convertible_to<RQ, preview::common_reference_t<typename R::type&, TQ>>;

#endif

namespace preview_refwrap_to_std_refwrap {

template<typename T>
std::reference_wrapper<T> ref(preview::reference_wrapper<T> x) {
return std::reference_wrapper<T>(x.get());
}

template<typename T>
constexpr std::reference_wrapper<const T> cref(preview::reference_wrapper<T> t) noexcept {
return std::reference_wrapper<const T>(t.get());
}

} // namespace preview_refwrap_to_std_refwrap
} // namespace detail
} // namespace preview

namespace std {

using preview::detail::preview_refwrap_to_std_refwrap::ref;
using preview::detail::preview_refwrap_to_std_refwrap::cref;

} // namespace std

#if PREVIEW_CONFORM_CXX20_STANDARD

template<class R, class T, template<class> class RQual, template<class> class TQual>
requires (preview::detail::ref_wrap_common_reference_exists_with<R, T, RQual<R>, TQual<T>> &&
!preview::detail::ref_wrap_common_reference_exists_with<T, R, TQual<T>, RQual<R>>)
struct std::basic_common_reference<R, T, RQual, TQual> {
using type = std::common_reference_t<typename R::type&, TQual<T>>;
};

template<class T, class R, template<class> class TQual, template<class> class RQual>
requires (preview::detail::ref_wrap_common_reference_exists_with<R, T, RQual<R>, TQual<T>> &&
!preview::detail::ref_wrap_common_reference_exists_with<T, R, TQual<T>, RQual<R>>)
struct std::basic_common_reference<T, R, TQual, RQual> {
using type = std::common_reference_t<typename R::type&, TQual<T>>;
};

#endif

#endif // PREVIEW_FUNCTIONAL_REFERENCE_WRAPPER_H_
16 changes: 2 additions & 14 deletions include/preview/__ranges/ref_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "preview/__concepts/convertible_to.h"
#include "preview/__concepts/different_from.h"
#include "preview/__functional/reference_wrapper.h"
#include "preview/__memory/addressof.h"
#include "preview/__ranges/contiguous_range.h"
#include "preview/__ranges/enable_borrowed_range.h"
Expand All @@ -26,19 +27,6 @@

namespace preview {
namespace ranges {
namespace detail {
namespace ref_view_fn {

template<typename R> static void fun(R&);
template<typename R> static void fun(R&&) = delete;

template<typename R, typename T, typename = void>
struct fallback_to_lref : std::false_type {};
template<typename R, typename T>
struct fallback_to_lref<R, T, void_t<decltype(fun<R>(std::declval<T>()))>> : std::true_type {};

} // namespace ref_view_fn
} // namespace detail

template<typename R>
class ref_view : public view_interface<ref_view<R>> {
Expand All @@ -49,7 +37,7 @@ class ref_view : public view_interface<ref_view<R>> {
template<typename T, std::enable_if_t<conjunction<
different_from<T, ref_view>,
convertible_to<T, R&>,
detail::ref_view_fn::fallback_to_lref<R, T>
preview::detail::can_bind_to_reference<T, R>
>::value, int> = 0>
PREVIEW_CONSTEXPR_AFTER_CXX17 ref_view(T&& t) noexcept
: r_(preview::addressof(static_cast<R&>(std::forward<T>(t)))) {}
Expand Down
Loading

0 comments on commit 5301254

Please sign in to comment.