From df3b7cde8d404227a16c1e5d719e59739404b776 Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 9 Sep 2022 17:39:17 -0400 Subject: [PATCH] own --- CMakeLists.txt | 3 + construct/make_default.h | 25 +-- num/float_concepts.h | 33 ++++ num/unsigned_integer.h | 14 ++ option/option.h | 4 +- ptr/__private/in_use.h | 59 +++++++ ptr/own.h | 322 +++++++++++++++++++++++++++++++++++++ ptr/own_unittest.cc | 335 +++++++++++++++++++++++++++++++++++++++ ptr/ptr_concepts.h | 31 ++++ 9 files changed, 813 insertions(+), 13 deletions(-) create mode 100644 num/float_concepts.h create mode 100644 ptr/__private/in_use.h create mode 100644 ptr/own.h create mode 100644 ptr/own_unittest.cc create mode 100644 ptr/ptr_concepts.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 43a42faaf..8d4c777ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ add_library(subspace STATIC "num/__private/signed_integer_macros.h" "num/__private/unsigned_integer_macros.h" "num/float.h" + "num/float_concepts.h" "num/fp_category.h" "num/signed_integer.h" "num/integer_concepts.h" @@ -81,6 +82,7 @@ add_library(subspace STATIC "option/__private/storage.h" "option/option.h" "option/state.h" + "ptr/own.h" "result/__private/is_result_type.h" "result/result.h" "tuple/__private/storage.h" @@ -136,6 +138,7 @@ add_executable(subspace_unittests "num/usize_unittest.cc" "option/option_unittest.cc" "option/option_types_unittest.cc" + "ptr/own_unittest.cc" "result/result_unittest.cc" "result/result_types_unittest.cc" "tuple/tuple_unittest.cc" diff --git a/construct/make_default.h b/construct/make_default.h index a845371b8..2515358c8 100644 --- a/construct/make_default.h +++ b/construct/make_default.h @@ -21,23 +21,17 @@ namespace sus::construct { namespace __private { template -constexpr inline bool has_with_default(...) { - return false; -} - -template - requires(std::same_as()...)), T>) -constexpr inline bool has_with_default(int) { - return true; -} +concept HasWithDefault = requires { + { T::with_default() } -> std::same_as; +}; } // namespace __private // clang-format off template concept MakeDefault = - (std::constructible_from && !__private::has_with_default(0)) || - (!std::constructible_from && __private::has_with_default(0)); + (std::constructible_from && !__private::HasWithDefault) || + (!std::constructible_from && __private::HasWithDefault); // clang-format on template @@ -48,4 +42,13 @@ inline constexpr T make_default() noexcept { return T::with_default(); } +template + requires(std::is_move_constructible_v) +inline T* alloc_make_default() noexcept { + if constexpr (std::constructible_from) + return new T(); + else + return new T(T::with_default()); +} + } // namespace sus::construct diff --git a/num/float_concepts.h b/num/float_concepts.h new file mode 100644 index 000000000..82fea314b --- /dev/null +++ b/num/float_concepts.h @@ -0,0 +1,33 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +namespace sus::num { + +struct f32; +struct f64; + +template +concept FloatingPoint = + std::same_as> || std::same_as>; + +template +concept PrimitiveFloatingPoint = + std::same_as || + std::same_as || std::same_as; + +} // namespace sus::num diff --git a/num/unsigned_integer.h b/num/unsigned_integer.h index 9e1bf5325..a4772e533 100644 --- a/num/unsigned_integer.h +++ b/num/unsigned_integer.h @@ -16,6 +16,7 @@ #include +#include "marker/unsafe.h" #include "num/__private/unsigned_integer_macros.h" namespace sus::num { @@ -98,6 +99,19 @@ struct usize final { check(val <= uint64_t{MAX_PRIMITIVE}); } } + + // Constructs a `usize` from a pointer type. + template + static inline usize from_ptr(::sus::marker::UnsafeFnMarker, T* t) { + return reinterpret_cast().primitive_value)>(t); + } + + // Converts a `usize` into a pointer type. + template + requires(std::is_pointer_v) + inline T to_ptr(::sus::marker::UnsafeFnMarker) const { + return reinterpret_cast(primitive_value); + } }; } // namespace sus::num diff --git a/option/option.h b/option/option.h index 752bc2240..545c3287e 100644 --- a/option/option.h +++ b/option/option.h @@ -107,10 +107,10 @@ class Option final { /// /// The Option's contained type `T` must be #MakeDefault, and will be /// constructed through that trait. - static inline constexpr Option with_default() noexcept + static inline constexpr Option with_default() noexcept requires(::sus::construct::MakeDefault) { - return Option(::sus::construct::make_default()); + return Option(::sus::construct::make_default()); } /// Destructor for the Option. diff --git a/ptr/__private/in_use.h b/ptr/__private/in_use.h new file mode 100644 index 000000000..ca26ba67e --- /dev/null +++ b/ptr/__private/in_use.h @@ -0,0 +1,59 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "macros/always_inline.h" +#include "mem/relocate.h" +#include "ptr/ptr_concepts.h" + +namespace sus::ptr { +template +class [[sus_trivial_abi]] Own; +} + +namespace sus::ptr::__private { + +template +class InUse { + public: + constexpr sus_always_inline ~InUse() noexcept { own.t_ = &t; } + + constexpr sus_always_inline const auto* operator->() const&& noexcept + requires(HasArrow) + { + return t.operator->(); + } + constexpr sus_always_inline auto* operator->() && noexcept + requires(HasArrowMut) + { + return t.operator->(); + } + + constexpr sus_always_inline const auto* operator->() const&& noexcept + requires(!HasArrowMut) + { + return &t; + } + constexpr sus_always_inline auto* operator->() && noexcept + requires(!HasArrowMut) + { + return &t; + } + + T& t; + Own& own; +}; + +} // namespace sus::ptr::__private diff --git a/ptr/own.h b/ptr/own.h new file mode 100644 index 000000000..72f4f0089 --- /dev/null +++ b/ptr/own.h @@ -0,0 +1,322 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "assertions/builtin.h" +#include "assertions/check.h" +#include "mem/forward.h" +#include "mem/relocate.h" +#include "mem/replace.h" +#include "num/float_concepts.h" +#include "num/integer_concepts.h" +#include "num/unsigned_integer.h" +#include "ptr/__private/in_use.h" + +namespace sus::ptr { + +namespace __private { + +// Pointer is set to a value in this mask when moved from or in use through +// operator->. +static constexpr auto kInUsePtr = 0x00000001_usize; +// Compare these bits to 0 to see if pointer is not moved-from or in use. +static constexpr auto kValidMask = 0xfffffff0_usize; + +} // namespace __private + +template +class [[sus_trivial_abi]] Own final { + sus_class_trivial_relocatable_value(unsafe_fn, true); + + static_assert(!std::is_reference_v, + "own must hold a concrete object and not a reference"); + static_assert(!std::is_pointer_v>, + "own must hold a concrete object and not a pointer"); + + static constexpr bool T_is_primitive = + ::sus::num::Integer || ::sus::num::PrimitiveInteger || + ::sus::num::FloatingPoint || ::sus::num::PrimitiveFloatingPoint || + std::is_enum_v; + + public: + template + static inline Own with(Args&&... args) noexcept { + return Own(kConstructArgs, forward(args)...); + } + + /// Conversion from T to Own. + /// + /// The conversion allocates on the heap and moves t from the stack into it. + static constexpr Own from(T t) { + // Sets `o` as moved from. + return Own(kConstructArgs, static_cast(t)); + } + + // Conversion from Own to Own. + static constexpr Own from(Own>&& o) + requires(std::is_const_v) + { + check(o.is_not_in_use_or_moved_from()); + // Sets `o` as moved from. + return Own(kConstructPointer, ::sus::mem::replace_ptr(mref(o.t_), nullptr)); + } + + /// Constructs an `Own` from a raw pointer `T*`. + /// + /// After calling this function, the raw pointer is owned by the resulting + /// `Own`. Specifically, the `Own` destructor will call the destructor + /// of `T` and free the allocated memory. + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to memory problems. + /// For example, a double-free may occur if the function is called twice on + /// the same raw pointer. + static inline Own from_raw(::sus::marker::UnsafeFnMarker, T* raw) noexcept { + return Own(kConstructPointer, raw); + } + + /// Construct an `Own` with the default constructor for the type `T`. + /// + /// The type `T` must be #MakeDefault, and will be constructed through that + /// trait. + static inline Own with_default() noexcept + requires(::sus::construct::MakeDefault) + { + return Own(kConstructDefault); + } + + ~Own() noexcept { + if (is_not_moved_from()) { + check(is_not_in_use()); + delete t_; + } + } + + Own(const Own& o) = delete; + Own& operator=(const Own& o) = delete; + + constexpr Own(Own&& o) + // Sets `o` as moved from. + : t_(::sus::mem::replace_ptr(mref(o.t_), nullptr)) { + check(is_not_in_use_or_moved_from()); + } + + constexpr Own& operator=(Own&& o) { + check(o.is_not_in_use_or_moved_from()); + check(is_not_in_use()); + if (is_not_moved_from()) { + delete t_; + } + // Sets `o` as moved from. + t_ = ::sus::mem::replace_ptr(mref(o.t_), nullptr); + return *this; + } + + constexpr auto operator*() const noexcept + requires(T_is_primitive) + { + check(is_not_in_use_or_moved_from()); + return *t_; + } + + constexpr const auto operator->() const noexcept { +#if __has_builtin(__builtin_prefetch) + __builtin_prefetch(t_); +#endif + check(is_not_in_use_or_moved_from()); + // `t_` will be put back and the class can't be used in the meantime. So + // this method acts like const externally, but needs to mutate `t_` to + // provide safety guarnatees. + Own& this_mut = const_cast&>(*this); + return __private::InUse( + *mem::replace_ptr(mref(this_mut.t_), in_use_ptr()), this_mut); + } + + auto operator->() noexcept { +#if __has_builtin(__builtin_prefetch) + __builtin_prefetch(t_); +#endif + check(is_not_in_use_or_moved_from()); + return __private::InUse(*mem::replace_ptr(mref(t_), in_use_ptr()), + *this); + } + + void drop() && noexcept { + check(is_not_in_use_or_moved_from()); + delete t_; + t_ = nullptr; // Is moved from. + } + + // Upcasting from Own to Own. + template + requires(std::is_convertible_v) + constexpr Own cast_to() && noexcept { +#if __has_builtin(__builtin_prefetch) + __builtin_prefetch(t_); +#endif + check(is_not_in_use_or_moved_from()); + return Own(Own::kConstructPointer, + static_cast(mem::replace_ptr(mref(t_), nullptr))); + } + + // TODO: downcast, downcast_unchecked + + Own clone() const noexcept { + check(is_not_in_use_or_moved_from()); + return Own(kConstructCloning, *t_); + } + + // TODO: ptr + + constexpr bool ptr_equal(const Own& o) const noexcept { + check(is_not_in_use_or_moved_from()); + check(o.is_not_in_use_or_moved_from()); + return t_ == o.t_; + } + + /// This copies-from the stored T, not from the Own. No deallocation + /// occurs. + constexpr T to_copy() const noexcept { + check(is_not_in_use_or_moved_from()); + return *t_; + } + + /// This copies into the stored T. No allocation occurs. + template + requires(std::is_assignable_v) + constexpr void copy_from(const U& u) noexcept { + check(is_not_in_use_or_moved_from()); + *t_ = u; + } + + /// This moves-from the stored T, not from the Own. No deallocation occurs. + /// + /// The Own remains valid though the T inside will be in a moved-from state + /// and should be reinitialized by copy_to() or move_to() before use.constexpr + T to_move() noexcept { + check(is_not_in_use_or_moved_from()); + return static_cast(*t_); + } + + /// This moves into the stored T. No allocation occurs. + template + requires(std::is_assignable_v && + (std::is_rvalue_reference_v || !std::is_reference_v) && + !std::is_const_v) + constexpr void move_from(U&& u) noexcept { + check(is_not_in_use_or_moved_from()); + *t_ = static_cast(u); + } + + /// Copy or move-assigns to the underlying T. No allocation occurs. + /// + /// Prefer copy_to() or move_to(), as this is for use from templated code + /// which has a forwarding reference to T. + template + requires(std::is_assignable_v) + constexpr void forward_from(U&& u) noexcept { + check(is_not_in_use_or_moved_from()); + *t_ = forward(u); + } + + T* into_raw(::sus::marker::UnsafeFnMarker) && noexcept { + check(is_not_in_use_or_moved_from()); + return mem::replace_ptr(mref(t_), nullptr); // Is moved from. + } + + const T& as_ref(::sus::marker::UnsafeFnMarker) const& noexcept { + check(is_not_in_use_or_moved_from()); + return *t_; + } + const T& as_ref(::sus::marker::UnsafeFnMarker) && = delete; + + const T& as_mut(::sus::marker::UnsafeFnMarker) & noexcept { + check(is_not_in_use_or_moved_from()); + return *t_; + } + + private: + friend class __private::InUse; // To remove the in-use flag when done. + template + friend class Own; + + enum ConstructArgs { kConstructArgs }; + template + explicit inline Own(ConstructArgs, Args&&... args) noexcept + // In use during the constructor. + : t_(in_use_ptr()) { + t_ = new T(forward(args)...); + } + + enum ConstructDefault { kConstructDefault }; + explicit inline Own(ConstructDefault) noexcept + // In use during the constructor. + : t_(in_use_ptr()) { + t_ = ::sus::construct::alloc_make_default(); + } + enum ConstructPointer { kConstructPointer }; + explicit inline Own(ConstructPointer, T* t) noexcept : t_(t) {} + enum ConstructCloning { kConstructCloning }; + explicit inline Own(ConstructCloning, const T& t) noexcept + // In use during the constructor. + : t_(in_use_ptr()) { + t_ = new T(t); // TODO: Use a clone concept. + } + + constexpr sus_always_inline bool is_not_in_use() const noexcept { + return usize::from_ptr(unsafe_fn, t_) != __private::kInUsePtr; + } + constexpr sus_always_inline bool is_not_moved_from() const noexcept { + return bool(t_); + } + constexpr sus_always_inline bool is_not_in_use_or_moved_from() + const noexcept { + return (usize::from_ptr(unsafe_fn, t_) & __private::kValidMask) != 0_usize; + } + static constexpr sus_always_inline T* in_use_ptr() noexcept { + return __private::kInUsePtr.to_ptr(unsafe_fn); + } + + /// The object owned by the Own, or one of: + /// - nullptr: Indicates the Own is moved-from. + /// - kInUsePtr: Indicates a method on T is being run. + T* t_; +}; + +/// sus::num::Eq> trait. +template + requires(::sus::num::Eq) +constexpr inline bool operator==(const Own& l, const Own& r) noexcept { + return l.as_ref(unsafe_fn) == r.as_ref(unsafe_fn); +} + +/// sus::num::Ord> trait. +/// sus::num::WeakOrd> trait. +/// sus::num::PartialOrd> trait. +template + requires(::sus::num::PartialOrd) +constexpr inline auto operator<=>(const Own& l, const Own& r) noexcept { + return l.as_ref(unsafe_fn) <=> r.as_ref(unsafe_fn); +} + +} // namespace sus::ptr + +// Promote `ptr::Own` into the `sus` namespace. +namespace sus { +using ::sus::ptr::Own; +} diff --git a/ptr/own_unittest.cc b/ptr/own_unittest.cc new file mode 100644 index 000000000..455358f15 --- /dev/null +++ b/ptr/own_unittest.cc @@ -0,0 +1,335 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ptr/own.h" + +#include + +#include "mem/move.h" +#include "num/types.h" +#include "option/option.h" +#include "third_party/googletest/googletest/include/gtest/gtest.h" + +namespace { + +using sus::Own; + +struct S { + explicit S(usize& muts, usize& consts) : muts_(muts), consts_(consts) {} + + void Method() { muts_ += 1_usize; } + void Method() const { consts_ += 1_usize; } + + private: + usize& muts_; + usize& consts_; +}; + +struct SArrow { + explicit SArrow(usize& muts, usize& consts) : s(muts, consts) {} + + const S* operator->() const { return &s; } + S* operator->() { return &s; } + + S s; +}; + +TEST(Own, Arrow) { + auto muts = 0_usize; + auto consts = 0_usize; + { + auto o = Own::with(muts, consts); + o->Method(); + EXPECT_EQ(muts, 1_usize); + } + muts = consts = 0_usize; + { + const auto o = Own::with(muts, consts); + o->Method(); + EXPECT_EQ(consts, 1_usize); + } + muts = consts = 0_usize; + { + auto o = Own::with(muts, consts); + o->Method(); + EXPECT_EQ(consts, 1_usize); + } + muts = consts = 0_usize; + { + auto o = Own::with(muts, consts); + o->Method(); + EXPECT_EQ(muts, 1_usize); + } + muts = consts = 0_usize; + { + const auto o = Own::with(muts, consts); + o->Method(); + EXPECT_EQ(consts, 1_usize); + } + muts = consts = 0_usize; + { + auto o = Own::with(muts, consts); + o->Method(); + EXPECT_EQ(consts, 1_usize); + } +} + +class DefaultConstruct {}; +class WithDefaultConstruct { + public: + static auto with_default() noexcept { return WithDefaultConstruct(); } + + private: + WithDefaultConstruct() {} +}; + +template +concept HasMakeDefault = requires { + { Own::with_default() }; + }; + +static_assert(!HasMakeDefault); +static_assert(HasMakeDefault); +static_assert(HasMakeDefault); + +struct FreeCounter { + FreeCounter(usize& frees) : frees_(frees) {} + ~FreeCounter() { frees_ += 1_usize; } + + private: + usize& frees_; +}; + +TEST(OwnDeath, DestroyInConstruct) { + struct DeleteIt; + struct S { + Own own = Own::with(this); + }; + struct DeleteIt { + DeleteIt(S* s) { delete s; } + }; + +#if GTEST_HAS_DEATH_TEST + auto s = sus::Option>::none(); + EXPECT_DEATH(s = sus::Option>::with_default(), ""); +#endif +} + +TEST(Own, Freed) { + auto frees = 0_usize; + { + auto o = Own::with(frees); + EXPECT_EQ(frees, 0_usize); + } + EXPECT_EQ(frees, 1_usize); +} + +TEST(Own, Drop) { + auto frees = 0_usize; + auto o = Own::with(frees); + EXPECT_EQ(frees, 0_usize); + sus::move(o).drop(); + EXPECT_EQ(frees, 1_usize); +} + +TEST(Own, ToConst) { + { + auto o = Own::with(2_usize); + const Own oc(sus::move(o)); + EXPECT_EQ(*oc, 2_usize); + } + { + auto o = Own::with(2_usize); + auto oc = Own::from(sus::move(o)); + EXPECT_EQ(*oc, 2_usize); + } +} + +TEST(Own, Upcast) { + struct Base { + i32 i = 3_i32; + }; + struct Sub : Base {}; + auto sub = Own::with(); + auto base = sus::move(sub).cast_to(); + EXPECT_EQ(base->i, 3_i32); +} + +TEST(Own, CloneCopyable) { + auto o1 = Own::with(2_i32); + auto o2 = o1.clone(); + EXPECT_EQ(*o1, *o2); +} + +TEST(Own, FromRaw) { + auto frees = 0_usize; + { + auto o = Own::from_raw(unsafe_fn, new FreeCounter(frees)); + EXPECT_EQ(frees, 0_usize); + } + EXPECT_EQ(frees, 1_usize); +} + +TEST(Own, IntoRaw) { + auto frees = 0_usize; + FreeCounter* f; + { + auto o = Own::with(frees); + EXPECT_EQ(frees, 0_usize); + f = sus::move(o).into_raw(unsafe_fn); + } + EXPECT_EQ(frees, 0_usize); + delete f; + EXPECT_EQ(frees, 1_usize); +} + +TEST(Own, Eq) { + static_assert(sus::num::Eq); + static_assert(sus::num::Eq, Own>); + static_assert(!sus::num::Eq); + static_assert(!sus::num::Eq, Own>); + + auto a = Own::from(2_i32); + auto b = Own::from(2_i32); + auto c = Own::from(-2_i32); + EXPECT_EQ(a, b); + EXPECT_NE(b, c); +} + +TEST(Own, Ord) { + static_assert(sus::num::Ord); + static_assert(sus::num::Ord, Own>); + static_assert(!sus::num::Ord); + static_assert(!sus::num::Ord, Own>); + + EXPECT_GT(Own::from(2_i32), Own::from(-2_i32)); +} + +TEST(Own, StrongOrder) { + EXPECT_EQ(std::strong_order(Own::from(2_i32), Own::from(2_i32)), + std::strong_ordering::equal); + EXPECT_EQ(std::strong_order(Own::from(2_i32), Own::from(3_i32)), + std::strong_ordering::less); + EXPECT_EQ(std::strong_order(Own::from(2_i32), Own::from(1_i32)), + std::strong_ordering::greater); +} + +struct Weak { + auto operator==(Weak const& o) const& { return a == o.a && b == o.b; } + auto operator<=>(Weak const& o) const& { + if (a == o.a) return std::weak_ordering::equivalent; + if (a < o.a) return std::weak_ordering::less; + return std::weak_ordering::greater; + } + + Weak(int a, int b) : a(a), b(b) {} + int a; + int b; +}; + +TEST(Own, WeakOrder) { + static_assert(sus::num::WeakOrd); + static_assert(sus::num::WeakOrd, Own>); + static_assert(!sus::num::WeakOrd); + static_assert(!sus::num::WeakOrd, Own>); + + EXPECT_EQ( + std::weak_order(Own::from(Weak(1, 2)), Own::from(Weak(1, 2))), + std::weak_ordering::equivalent); + EXPECT_EQ( + std::weak_order(Own::from(Weak(1, 2)), Own::from(Weak(1, 3))), + std::weak_ordering::equivalent); + EXPECT_EQ( + std::weak_order(Own::from(Weak(1, 2)), Own::from(Weak(2, 3))), + std::weak_ordering::less); + EXPECT_EQ( + std::weak_order(Own::from(Weak(2, 2)), Own::from(Weak(1, 3))), + std::weak_ordering::greater); +} + +TEST(Own, PartialOrder) { + EXPECT_EQ( + std::partial_order(Own::from(11_f32), Own::from(11.2_f32)), + std::partial_ordering::less); + EXPECT_EQ(std::partial_order(Own::from(11_f32), + Own::from(f32::TODO_NAN())), + std::partial_ordering::unordered); +} + +TEST(Own, PtrEqual) { + auto a = Own::from(2_i32); + auto b = Own::from(2_i32); + EXPECT_EQ(a.ptr_equal(b), false); + EXPECT_EQ(a.ptr_equal(a), true); +} + +TEST(Own, ToCopy) { + auto o = Own::from(2_i32); + auto i = o.to_copy(); + EXPECT_EQ(i, *o); +} + +TEST(Own, CopyFrom) { + auto o = Own::from(2_i32); + auto i = 3_i32; + o.copy_from(i); + EXPECT_EQ(i, *o); +} + +struct Mover { + i32 i = 2_i32; + Mover() {} + + Mover(Mover&& m) : i(m.i) { m.i = 0_i32; } + void operator=(Mover&& m) { i = sus::mem::replace(mref(m.i), 0_i32); } + + Mover(const Mover& m) : i(m.i) {} + void operator=(const Mover& m) { i = m.i; } +}; + +TEST(Own, ToMove) { + auto o = Own::with(); + EXPECT_EQ(o->i, 2_i32); + auto m = o.to_move(); + EXPECT_EQ(m.i, 2_i32); + EXPECT_EQ(o->i, 0_i32); +} + +TEST(Own, MoveFrom) { + auto o = Own::with(); + auto m = Mover(); + m.i = 3_i32; + EXPECT_EQ(o->i, 2_i32); + o.move_from(sus::move(m)); + EXPECT_EQ(o->i, 3_i32); + EXPECT_EQ(m.i, 0_i32); +} + +TEST(Own, ForwardFrom) { + auto o = Own::with(); + auto m = Mover(); + + m.i = 3_i32; + EXPECT_EQ(o->i, 2_i32); + o.forward_from(static_cast(m)); + EXPECT_EQ(o->i, 3_i32); + EXPECT_EQ(m.i, 3_i32); + + m.i = 4_i32; + o.forward_from(sus::move(m)); + EXPECT_EQ(o->i, 4_i32); + EXPECT_EQ(m.i, 0_i32); +} + +} // namespace diff --git a/ptr/ptr_concepts.h b/ptr/ptr_concepts.h new file mode 100644 index 000000000..afa67f9ee --- /dev/null +++ b/ptr/ptr_concepts.h @@ -0,0 +1,31 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +namespace sus::ptr { + +// clang-format off +template +concept HasArrow = requires (const T& t) { + { t.operator->() }; +}; + +template +concept HasArrowMut = requires (T& t) { + { t.operator->() }; +}; +// clang-format on + +} // namespace sus::ptr