diff --git a/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.hpp b/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.hpp index 16a5d4ad7f6..f8c93323e80 100644 --- a/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.hpp +++ b/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.hpp @@ -11,6 +11,7 @@ #error "Assumes that ARC is not used" #endif +#include #include #include @@ -20,35 +21,35 @@ OBJC_FORWARD_DECLARE(NSObject); namespace kotlin::objc_support { -struct object_ptr_retain_t{}; +struct object_ptr_retain_t {}; inline constexpr object_ptr_retain_t object_ptr_retain{}; namespace internal { -class ObjectPtrImpl { +class NSObjectPtrImpl { public: - ObjectPtrImpl() noexcept; - explicit ObjectPtrImpl(NSObject* object) noexcept; - ObjectPtrImpl(object_ptr_retain_t, NSObject* object) noexcept; + NSObjectPtrImpl() noexcept; + explicit NSObjectPtrImpl(NSObject* object) noexcept; + NSObjectPtrImpl(object_ptr_retain_t, NSObject* object) noexcept; - ObjectPtrImpl(const ObjectPtrImpl& rhs) noexcept; - ObjectPtrImpl(ObjectPtrImpl&& rhs) noexcept; + NSObjectPtrImpl(const NSObjectPtrImpl& rhs) noexcept; + NSObjectPtrImpl(NSObjectPtrImpl&& rhs) noexcept; - ~ObjectPtrImpl(); + ~NSObjectPtrImpl(); - ObjectPtrImpl& operator=(const ObjectPtrImpl& rhs) noexcept { - ObjectPtrImpl tmp(rhs); + NSObjectPtrImpl& operator=(const NSObjectPtrImpl& rhs) noexcept { + NSObjectPtrImpl tmp(rhs); swap(tmp); return *this; } - ObjectPtrImpl& operator=(ObjectPtrImpl&& rhs) noexcept { - ObjectPtrImpl tmp(std::move(rhs)); + NSObjectPtrImpl& operator=(NSObjectPtrImpl&& rhs) noexcept { + NSObjectPtrImpl tmp(std::move(rhs)); swap(tmp); return *this; } - void swap(ObjectPtrImpl& rhs) noexcept; + void swap(NSObjectPtrImpl& rhs) noexcept; NSObject* get() const noexcept; bool valid() const noexcept; @@ -57,20 +58,60 @@ public: void reset(NSObject* object) noexcept; void reset(object_ptr_retain_t, NSObject* object) noexcept; - bool operator==(const ObjectPtrImpl& rhs) const noexcept; - bool operator<(const ObjectPtrImpl& rhs) const noexcept; - - std::size_t computeHash() const noexcept; + bool operator==(const NSObjectPtrImpl& rhs) const noexcept; + bool operator<(const NSObjectPtrImpl& rhs) const noexcept; private: NSObject* object_; }; +class CFObjectPtrImpl { +public: + CFObjectPtrImpl() noexcept : object_(nullptr) {} + explicit CFObjectPtrImpl(CFTypeRef object) noexcept : object_(object) {} + CFObjectPtrImpl(object_ptr_retain_t, CFTypeRef object) noexcept : object_(CFRetain(object)) {} + + CFObjectPtrImpl(const CFObjectPtrImpl& rhs) noexcept : object_(CFRetain(rhs.object_)) {} + CFObjectPtrImpl(CFObjectPtrImpl&& rhs) noexcept : object_(rhs.object_) { rhs.object_ = nullptr; } + + ~CFObjectPtrImpl(); + + CFObjectPtrImpl& operator=(const CFObjectPtrImpl& rhs) noexcept { + CFObjectPtrImpl tmp(rhs); + swap(tmp); + return *this; + } + + CFObjectPtrImpl& operator=(CFObjectPtrImpl&& rhs) noexcept { + CFObjectPtrImpl tmp(std::move(rhs)); + swap(tmp); + return *this; + } + + void swap(CFObjectPtrImpl& rhs) noexcept { + using std::swap; + swap(object_, rhs.object_); + } + + CFTypeRef get() const noexcept { return object_; } + bool valid() const noexcept { return object_ != nullptr; } + + void reset() noexcept { reset(nullptr); } + void reset(CFTypeRef object) noexcept; + void reset(object_ptr_retain_t, CFTypeRef object) noexcept { reset(CFRetain(object)); } + + bool operator==(const CFObjectPtrImpl& rhs) const noexcept { return object_ == rhs.object_; } + bool operator<(const CFObjectPtrImpl& rhs) const noexcept { return std::less<>()(object_, rhs.object_); } + +private: + CFTypeRef object_; +}; + } // namespace internal -// `std::shared_ptr`-like smart pointer for ObjC objects. +// `std::shared_ptr`-like smart pointer for ObjC/CF objects. // -// IMPORTANT: By default constructed from a retained ObjC object. +// IMPORTANT: By default constructed from a retained ObjC/CF object. // This optimizes for the common case of constructing directly from `[[T alloc] init...]`. // To construct and additionally retain the object, use `object_ptr_retain` as the first argument // to the constructor. @@ -86,23 +127,28 @@ private: // When transfering ownership, prefer to move because it avoids calling `retain` and `release`. template class object_ptr { + // All CF types are known only as CF...Ref which are always pointers. + static inline constexpr bool useCF = std::is_pointer_v; + public: + using element_type = std::conditional_t, T>; + // Construct empty `object_ptr`. object_ptr() noexcept = default; - // Construct from ObjC object. + // Construct from ObjC/CF object. // // IMPORTANT: this does not `retain` `ptr`. - explicit object_ptr(T* ptr) noexcept : impl_(ptr) {} + explicit object_ptr(element_type* ptr) noexcept : impl_(ptr) {} - // Construct from ObjC object, additionally retaining `ptr`. - object_ptr(object_ptr_retain_t, T* ptr) noexcept : impl_(object_ptr_retain, ptr) {} + // Construct from ObjC/CF object, additionally retaining `ptr`. + object_ptr(object_ptr_retain_t, element_type* ptr) noexcept : impl_(object_ptr_retain, ptr) {} void swap(object_ptr& rhs) noexcept { impl_.swap(rhs.impl_); } - T* get() const noexcept { return (T*)impl_.get(); } - T* operator*() const noexcept { return (T*)impl_.get(); } - T* operator->() const noexcept { return (T*)impl_.get(); } + element_type* get() const noexcept { return (element_type*)impl_.get(); } + element_type* operator*() const noexcept { return (element_type*)impl_.get(); } + element_type* operator->() const noexcept { return (element_type*)impl_.get(); } explicit operator bool() const noexcept { return impl_.valid(); } @@ -112,10 +158,10 @@ public: // Release stored pointer and store `ptr` instead. // // IMPORTANT: this does not `retain` `ptr`. - void reset(T* ptr) noexcept { impl_.reset(ptr); } + void reset(element_type* ptr) noexcept { impl_.reset(ptr); } // Release stored pointer and store `ptr` instead, additionally retaining `ptr`. - void reset(object_ptr_retain_t, T* ptr) noexcept { impl_.reset(object_ptr_retain, ptr); } + void reset(object_ptr_retain_t, element_type* ptr) noexcept { impl_.reset(object_ptr_retain, ptr); } template bool operator==(const object_ptr& rhs) const noexcept { @@ -133,7 +179,7 @@ private: template friend class object_ptr; - internal::ObjectPtrImpl impl_; + std::conditional_t impl_; }; template @@ -167,7 +213,7 @@ void swap(kotlin::objc_support::object_ptr& lhs, kotlin::objc_support::object template struct hash> { - std::size_t operator()(const kotlin::objc_support::object_ptr& value) { return std::hash()(value.impl_.get()); } + std::size_t operator()(const kotlin::objc_support::object_ptr& value) { return std::hash()(value.impl_.get()); } }; // TODO: std::atomic specialization? diff --git a/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.mm b/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.mm index 50efe7480ac..61ffe9801d1 100644 --- a/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.mm +++ b/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtr.mm @@ -11,62 +11,75 @@ using namespace kotlin; -objc_support::internal::ObjectPtrImpl::ObjectPtrImpl() noexcept : object_(nil) {} +objc_support::internal::NSObjectPtrImpl::NSObjectPtrImpl() noexcept : object_(nil) {} -objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(NSObject* object) noexcept : object_(object) {} +objc_support::internal::NSObjectPtrImpl::NSObjectPtrImpl(NSObject* object) noexcept : object_(object) {} -objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(object_ptr_retain_t, NSObject* object) noexcept : object_([object retain]) {} +objc_support::internal::NSObjectPtrImpl::NSObjectPtrImpl(object_ptr_retain_t, NSObject* object) noexcept : object_([object retain]) {} -objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(const ObjectPtrImpl& rhs) noexcept : object_([rhs.object_ retain]) {} +objc_support::internal::NSObjectPtrImpl::NSObjectPtrImpl(const NSObjectPtrImpl& rhs) noexcept : object_([rhs.object_ retain]) {} -objc_support::internal::ObjectPtrImpl::ObjectPtrImpl(ObjectPtrImpl&& rhs) noexcept : object_(rhs.object_) { +objc_support::internal::NSObjectPtrImpl::NSObjectPtrImpl(NSObjectPtrImpl&& rhs) noexcept : object_(rhs.object_) { rhs.object_ = nil; } -objc_support::internal::ObjectPtrImpl::~ObjectPtrImpl() { +objc_support::internal::NSObjectPtrImpl::~NSObjectPtrImpl() { @autoreleasepool { [object_ release]; } } -void objc_support::internal::ObjectPtrImpl::swap(ObjectPtrImpl& rhs) noexcept { +void objc_support::internal::NSObjectPtrImpl::swap(NSObjectPtrImpl& rhs) noexcept { using std::swap; swap(object_, rhs.object_); } -NSObject* objc_support::internal::ObjectPtrImpl::get() const noexcept { +NSObject* objc_support::internal::NSObjectPtrImpl::get() const noexcept { return object_; } -bool objc_support::internal::ObjectPtrImpl::valid() const noexcept { +bool objc_support::internal::NSObjectPtrImpl::valid() const noexcept { return object_ != nil; } -void objc_support::internal::ObjectPtrImpl::reset() noexcept { +void objc_support::internal::NSObjectPtrImpl::reset() noexcept { reset(nil); } -void objc_support::internal::ObjectPtrImpl::reset(NSObject* object) noexcept { +void objc_support::internal::NSObjectPtrImpl::reset(NSObject* object) noexcept { @autoreleasepool { [object_ release]; object_ = object; } } -void objc_support::internal::ObjectPtrImpl::reset(object_ptr_retain_t, NSObject* object) noexcept { +void objc_support::internal::NSObjectPtrImpl::reset(object_ptr_retain_t, NSObject* object) noexcept { reset([object retain]); } -std::size_t objc_support::internal::ObjectPtrImpl::computeHash() const noexcept { - return object_.hash; -} - -bool objc_support::internal::ObjectPtrImpl::operator==(const ObjectPtrImpl& rhs) const noexcept { +bool objc_support::internal::NSObjectPtrImpl::operator==(const NSObjectPtrImpl& rhs) const noexcept { return object_ == rhs.object_; } -bool objc_support::internal::ObjectPtrImpl::operator<(const ObjectPtrImpl& rhs) const noexcept { +bool objc_support::internal::NSObjectPtrImpl::operator<(const NSObjectPtrImpl& rhs) const noexcept { return std::less<>()(object_, rhs.object_); } +objc_support::internal::CFObjectPtrImpl::~CFObjectPtrImpl() { + if (object_) { + @autoreleasepool { + CFRelease(object_); + } + } +} + +void objc_support::internal::CFObjectPtrImpl::reset(CFTypeRef object) noexcept { + if (object_) { + @autoreleasepool { + CFRelease(object_); + } + } + object_ = object; +} + #endif diff --git a/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtrTest.mm b/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtrTest.mm index 23d724b6ea8..38622a1354c 100644 --- a/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtrTest.mm +++ b/kotlin-native/runtime/src/main/cpp/objc_support/ObjectPtrTest.mm @@ -7,6 +7,8 @@ #include "ObjectPtr.hpp" +#import +#import #import #include #include @@ -61,145 +63,201 @@ private: @end +template +class ObjectPtrTest : public testing::Test {}; + +struct ObjectPtrTestCF { + using TestedType = objc_support::object_ptr; + using OtherType = objc_support::object_ptr; + + static CFArrayRef withDestructorHook(std::function hook) noexcept { + auto* contents = [[WithDestructorHookObjC alloc] initWithDestructorHook:std::move(hook)]; + auto* array = @[contents]; + [contents release]; + return (__bridge CFArrayRef)array; + } + + static WithDestructorHook* getHook(CFArrayRef ptr) noexcept { + auto* array = (__bridge NSArray*)ptr; + return array[0].impl; + } + + static void release(CFArrayRef ptr) noexcept { CFRelease(ptr); } + + static CFArrayRef retain(CFArrayRef ptr) noexcept { + CFRetain(ptr); + return ptr; + } +}; + +struct ObjectPtrTestNS { + using TestedType = objc_support::object_ptr; + using OtherType = objc_support::object_ptr; + + static WithDestructorHookObjC* withDestructorHook(std::function hook) noexcept { + return [[WithDestructorHookObjC alloc] initWithDestructorHook:std::move(hook)]; + } + + static WithDestructorHook* getHook(WithDestructorHookObjC* ptr) noexcept { return ptr.impl; } + + static void release(WithDestructorHookObjC* ptr) noexcept { [ptr release]; } + + static WithDestructorHookObjC* retain(WithDestructorHookObjC* ptr) noexcept { return [ptr retain]; } +}; + +using ObjectPtrTestTypes = testing::Types; +class ObjectPtrTestNames { +public: + template + static std::string GetName(int) { + if constexpr (std::is_same_v) { + return "CF"; + } else if constexpr (std::is_same_v) { + return "NS"; + } + } +}; +TYPED_TEST_SUITE(ObjectPtrTest, ObjectPtrTestTypes, ObjectPtrTestNames); + #define EXPECT_CONTAINS(smartPtr, ptr) \ do { \ - EXPECT_THAT(static_cast(smartPtr), (ptr) != nil); \ + EXPECT_THAT(static_cast(smartPtr), (ptr) != nullptr); \ EXPECT_THAT(*(smartPtr), (ptr)); \ EXPECT_THAT((smartPtr).get(), (ptr)); \ } while (false) -TEST(ObjectPtrTest, DefaultCtor) { - objc_support::object_ptr obj; +TYPED_TEST(ObjectPtrTest, DefaultCtor) { + typename TypeParam::TestedType obj; EXPECT_CONTAINS(obj, nil); } -TEST(ObjectPtrTest, ObjectCtor) { +TYPED_TEST(ObjectPtrTest, ObjectCtor) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj(ptr); + typename TypeParam::TestedType obj(ptr); EXPECT_CONTAINS(obj, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, ObjectCtorWithRetain) { +TYPED_TEST(ObjectPtrTest, ObjectCtorWithRetain) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj(objc_support::object_ptr_retain, ptr); + typename TypeParam::TestedType obj(objc_support::object_ptr_retain, ptr); EXPECT_CONTAINS(obj, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))).Times(0); } testing::Mock::VerifyAndClearExpectations(&destructorHook); - EXPECT_CALL(destructorHook, Call(ptr.impl)); - [ptr release]; + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); + TypeParam::release(ptr); } -TEST(ObjectPtrTest, CopyCtor) { +TYPED_TEST(ObjectPtrTest, CopyCtor) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj1(ptr); - objc_support::object_ptr obj2(obj1); + typename TypeParam::TestedType obj1(ptr); + typename TypeParam::TestedType obj2(obj1); EXPECT_CONTAINS(obj1, ptr); EXPECT_CONTAINS(obj2, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))).Times(0); obj1.reset(); testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj1, nil); EXPECT_CONTAINS(obj2, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, MoveCtor) { +TYPED_TEST(ObjectPtrTest, MoveCtor) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj1(ptr); + typename TypeParam::TestedType obj1(ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0); - objc_support::object_ptr obj2(std::move(obj1)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))).Times(0); + typename TypeParam::TestedType obj2(std::move(obj1)); testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj1, nil); EXPECT_CONTAINS(obj2, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, CopyAssignmentIntoEmpty) { +TYPED_TEST(ObjectPtrTest, CopyAssignmentIntoEmpty) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj2; + typename TypeParam::TestedType obj2; { - objc_support::object_ptr obj1(ptr); + typename TypeParam::TestedType obj1(ptr); obj2 = obj1; EXPECT_CONTAINS(obj1, ptr); EXPECT_CONTAINS(obj2, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))).Times(0); } testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj2, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, CopyAssignmentIntoSet) { +TYPED_TEST(ObjectPtrTest, CopyAssignmentIntoSet) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj2(ptr2); + typename TypeParam::TestedType obj2(ptr2); { - objc_support::object_ptr obj1(ptr1); + typename TypeParam::TestedType obj1(ptr1); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); obj2 = obj1; testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj1, ptr1); EXPECT_CONTAINS(obj2, ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))).Times(0); } testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj2, ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, MoveAssignmentIntoEmpty) { +TYPED_TEST(ObjectPtrTest, MoveAssignmentIntoEmpty) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj2; + typename TypeParam::TestedType obj2; { - EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))).Times(0); - objc_support::object_ptr obj1(ptr); + typename TypeParam::TestedType obj1(ptr); obj2 = std::move(obj1); EXPECT_CONTAINS(obj1, nil); EXPECT_CONTAINS(obj2, ptr); @@ -208,23 +266,23 @@ TEST(ObjectPtrTest, MoveAssignmentIntoEmpty) { EXPECT_CONTAINS(obj2, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, MoveAssignmentIntoSet) { +TYPED_TEST(ObjectPtrTest, MoveAssignmentIntoSet) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj2(ptr2); + typename TypeParam::TestedType obj2(ptr2); { - objc_support::object_ptr obj1(ptr1); + typename TypeParam::TestedType obj1(ptr1); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); - EXPECT_CALL(destructorHook, Call(ptr1.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))).Times(0); obj2 = std::move(obj1); testing::Mock::VerifyAndClearExpectations(&destructorHook); @@ -235,142 +293,142 @@ TEST(ObjectPtrTest, MoveAssignmentIntoSet) { EXPECT_CONTAINS(obj2, ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); } testing::Mock::VerifyAndClearExpectations(&destructorHook); } -TEST(ObjectPtrTest, SwapEmptyEmpty) { - objc_support::object_ptr obj1; - objc_support::object_ptr obj2; +TYPED_TEST(ObjectPtrTest, SwapEmptyEmpty) { + typename TypeParam::TestedType obj1; + typename TypeParam::TestedType obj2; obj1.swap(obj2); EXPECT_CONTAINS(obj1, nil); EXPECT_CONTAINS(obj2, nil); } -TEST(ObjectPtrTest, SwapEmptySet) { +TYPED_TEST(ObjectPtrTest, SwapEmptySet) { testing::StrictMock> destructorHook; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj1; - objc_support::object_ptr obj2(ptr2); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj1; + typename TypeParam::TestedType obj2(ptr2); obj1.swap(obj2); EXPECT_CONTAINS(obj1, ptr2); EXPECT_CONTAINS(obj2, nil); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); } -TEST(ObjectPtrTest, SwapSetEmpty) { +TYPED_TEST(ObjectPtrTest, SwapSetEmpty) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj1(ptr1); - objc_support::object_ptr obj2; + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj1(ptr1); + typename TypeParam::TestedType obj2; obj1.swap(obj2); EXPECT_CONTAINS(obj1, nil); EXPECT_CONTAINS(obj2, ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); } -TEST(ObjectPtrTest, SwapSetSet) { +TYPED_TEST(ObjectPtrTest, SwapSetSet) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj1(ptr1); - objc_support::object_ptr obj2(ptr2); + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj1(ptr1); + typename TypeParam::TestedType obj2(ptr2); obj1.swap(obj2); EXPECT_CONTAINS(obj1, ptr2); EXPECT_CONTAINS(obj2, ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); } -TEST(ObjectPtrTest, ResetEmptyFromEmpty) { - objc_support::object_ptr obj; +TYPED_TEST(ObjectPtrTest, ResetEmptyFromEmpty) { + typename TypeParam::TestedType obj; obj.reset(); EXPECT_CONTAINS(obj, nil); } -TEST(ObjectPtrTest, ResetEmptyFromSet) { +TYPED_TEST(ObjectPtrTest, ResetEmptyFromSet) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj(ptr); + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj(ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); obj.reset(); testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj, nil); } -TEST(ObjectPtrTest, ResetObjectFromEmpty) { +TYPED_TEST(ObjectPtrTest, ResetObjectFromEmpty) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj; obj.reset(ptr); EXPECT_CONTAINS(obj, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); } -TEST(ObjectPtrTest, ResetObjectFromEmptyWithRetain) { +TYPED_TEST(ObjectPtrTest, ResetObjectFromEmptyWithRetain) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj; + typename TypeParam::TestedType obj; obj.reset(objc_support::object_ptr_retain, ptr); EXPECT_CONTAINS(obj, ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))).Times(0); } testing::Mock::VerifyAndClearExpectations(&destructorHook); - EXPECT_CALL(destructorHook, Call(ptr.impl)); - [ptr release]; + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); + TypeParam::release(ptr); } -TEST(ObjectPtrTest, ResetObjectFromSet) { +TYPED_TEST(ObjectPtrTest, ResetObjectFromSet) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj(ptr1); + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj(ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); obj.reset(ptr2); testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj, ptr2); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); } -TEST(ObjectPtrTest, ResetObjectFromSetWithRetain) { +TYPED_TEST(ObjectPtrTest, ResetObjectFromSetWithRetain) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); { - objc_support::object_ptr obj(ptr1); + typename TypeParam::TestedType obj(ptr1); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); obj.reset(objc_support::object_ptr_retain, ptr2); testing::Mock::VerifyAndClearExpectations(&destructorHook); EXPECT_CONTAINS(obj, ptr2); - EXPECT_CALL(destructorHook, Call(ptr2.impl)).Times(0); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))).Times(0); } testing::Mock::VerifyAndClearExpectations(&destructorHook); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); - [ptr2 release]; + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); + TypeParam::release(ptr2); } #define EXPECT_EQUAL(expr1, expr2) \ @@ -403,19 +461,19 @@ TEST(ObjectPtrTest, ResetObjectFromSetWithRetain) { EXPECT_TRUE((expr1) >= (expr2)); \ } while (false) -TEST(ObjectPtrTest, ComparisonsEmpty) { +TYPED_TEST(ObjectPtrTest, ComparisonsEmpty) { testing::StrictMock> destructorHook; - auto* ptr = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr empty; - objc_support::object_ptr reset(ptr); - EXPECT_CALL(destructorHook, Call(ptr.impl)); + auto* ptr = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType empty; + typename TypeParam::TestedType reset(ptr); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr))); reset.reset(); testing::Mock::VerifyAndClearExpectations(&destructorHook); - objc_support::object_ptr emptyOtherType; + typename TypeParam::OtherType emptyOtherType; EXPECT_EQUAL(empty, empty); - EXPECT_EQUAL(empty, objc_support::object_ptr()); + EXPECT_EQUAL(empty, typename TypeParam::TestedType()); EXPECT_EQUAL(empty, reset); EXPECT_EQUAL(empty, emptyOtherType); EXPECT_EQUAL(reset, empty); @@ -423,20 +481,20 @@ TEST(ObjectPtrTest, ComparisonsEmpty) { EXPECT_EQUAL(reset, emptyOtherType); } -TEST(ObjectPtrTest, ComparisonsEmptySet) { +TYPED_TEST(ObjectPtrTest, ComparisonsEmptySet) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr3 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj(ptr1); - objc_support::object_ptr empty; - objc_support::object_ptr reset(ptr2); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr3 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj(ptr1); + typename TypeParam::TestedType empty; + typename TypeParam::TestedType reset(ptr2); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); reset.reset(); testing::Mock::VerifyAndClearExpectations(&destructorHook); - objc_support::object_ptr emptyOtherType; - objc_support::object_ptr objOtherType(ptr3); + typename TypeParam::OtherType emptyOtherType; + typename TypeParam::TestedType objOtherType(ptr3); EXPECT_GREATER(obj, empty); EXPECT_GREATER(obj, reset); @@ -448,24 +506,24 @@ TEST(ObjectPtrTest, ComparisonsEmptySet) { EXPECT_GREATER(objOtherType, empty); EXPECT_GREATER(objOtherType, reset); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); - EXPECT_CALL(destructorHook, Call(ptr3.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr3))); } -TEST(ObjectPtrTest, ComparisonsSetSet) { +TYPED_TEST(ObjectPtrTest, ComparisonsSetSet) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); if (ptr2 < ptr1) { std::swap(ptr1, ptr2); } ASSERT_LT(ptr1, ptr2); - objc_support::object_ptr obj1(ptr1); - objc_support::object_ptr obj2(ptr2); - objc_support::object_ptr obj3(obj1); - objc_support::object_ptr objOtherType([ptr1 retain]); + typename TypeParam::TestedType obj1(ptr1); + typename TypeParam::TestedType obj2(ptr2); + typename TypeParam::TestedType obj3(obj1); + typename TypeParam::OtherType objOtherType(TypeParam::retain(ptr1)); EXPECT_EQUAL(obj1, obj1); EXPECT_LESS(obj1, obj2); @@ -484,30 +542,30 @@ TEST(ObjectPtrTest, ComparisonsSetSet) { EXPECT_EQUAL(objOtherType, obj3); EXPECT_EQUAL(objOtherType, objOtherType); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); } -TEST(ObjectPtrTest, Hashing) { +TYPED_TEST(ObjectPtrTest, Hashing) { testing::StrictMock> destructorHook; - auto* ptr1 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - auto* ptr2 = [[WithDestructorHookObjC alloc] initWithDestructorHook:destructorHook.AsStdFunction()]; - objc_support::object_ptr obj(ptr1); - objc_support::object_ptr empty; - objc_support::object_ptr reset(ptr2); - EXPECT_CALL(destructorHook, Call(ptr2.impl)); + auto* ptr1 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + auto* ptr2 = TypeParam::withDestructorHook(destructorHook.AsStdFunction()); + typename TypeParam::TestedType obj(ptr1); + typename TypeParam::TestedType empty; + typename TypeParam::TestedType reset(ptr2); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr2))); reset.reset(); testing::Mock::VerifyAndClearExpectations(&destructorHook); - using Hash = std::hash>; - using HashImpl = std::hash; + using Hash = std::hash; + using HashImpl = std::hash; EXPECT_THAT(Hash()(obj), HashImpl()(ptr1)); EXPECT_THAT(Hash()(empty), HashImpl()(nullptr)); EXPECT_THAT(Hash()(reset), HashImpl()(nullptr)); - EXPECT_CALL(destructorHook, Call(ptr1.impl)); + EXPECT_CALL(destructorHook, Call(TypeParam::getHook(ptr1))); } #endif