diff --git a/.codecov.yml b/.codecov.yml index bd061d04..1d20178a 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -14,5 +14,6 @@ comment: ignore: - "CxxTestProps/**" - "CxxTestUtils/**" + - "RTLTestRunApp/**" - "RTLBenchmarkApp/**" - "ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp" \ No newline at end of file diff --git a/RTLTestRunApp/src/RObjectTests/RObjectReflecting_stdUniquePtr.cpp b/RTLTestRunApp/src/RObjectTests/RObjectReflecting_stdUniquePtr.cpp index db416bf5..ca949a9f 100644 --- a/RTLTestRunApp/src/RObjectTests/RObjectReflecting_stdUniquePtr.cpp +++ b/RTLTestRunApp/src/RObjectTests/RObjectReflecting_stdUniquePtr.cpp @@ -8,6 +8,8 @@ using namespace test_utils; using namespace rtl; +// TODO: Refine these, remove moot cases after this-> UPDATE: MOVING DISALLOWED NOW. + namespace rtl::unit_test { TEST(RObject_reflecting_unique_ptr, clone_on__heap_stack) @@ -53,19 +55,18 @@ namespace rtl::unit_test auto view = robj.view>(); ASSERT_TRUE(view); - // get() - moves out the 'unique_ptr' from 'robj'. - // 'robj' still remains alive and type-consistent, but now holds an empty unique_ptr - std::unique_ptr uptr = view->get(); + // get() - UPDATE: MOVING DISALLOWED NOW. + const std::unique_ptr& uptr = view->get(); ASSERT_TRUE(uptr); + // RTL gave up the ownership after move-op. - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); + // Access the moved-out value EXPECT_EQ(*uptr, NUM); - int* ptr = uptr.release(); - // Addresses must be same. - EXPECT_EQ(numPtr, ptr); - delete ptr; //RTL must not delete again, once 'robj' out of scope. + // this is compile error now. + //int* ptr = uptr.release(); } // there must not be any crash. ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); @@ -89,19 +90,13 @@ namespace rtl::unit_test auto view = robj.view>(); ASSERT_TRUE(view); - // get() - moves out the 'unique_ptr' from 'robj'. - // 'robj' still remains alive and type-consistent, but now holds an empty unique_ptr - std::unique_ptr uptr = view->get(); + // get() - UPDATE: MOVING DISALLOWED NOW. + const std::unique_ptr& uptr = view->get(); ASSERT_TRUE(uptr); - // RTL gave up the ownership after move-op. - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); - // Access the moved-out value + // RTL still owns it. + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); + // Access the value EXPECT_EQ(uptr->data(), NUM); - - Node* ptr = uptr.release(); - // Addresses must be same. - EXPECT_TRUE(ptr->data() == NUM); - delete ptr; //RTL must not delete again, once 'robj' out of scope. } // there must not be any crash. ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); @@ -144,9 +139,8 @@ namespace rtl::unit_test // unique_ptr, ownership managed by RTL. ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); - // A move from any view transfers ownership of the underlying object - // RObject remains alive and type-consistent, but now holds an empty unique_ptr - std::unique_ptr uptr0 = view0->get(); + // get() - UPDATE: MOVING DISALLOWED NOW. + const std::unique_ptr& uptr0 = view0->get(); ASSERT_TRUE(uptr0); // Access the moved-out value @@ -155,191 +149,156 @@ namespace rtl::unit_test // Verify the original pointer address matches EXPECT_EQ(uptr0.get(), &valueNum); - // RTL gave up the ownership after move-op. - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); - - // RObject still exists with correct metadata, but the stored unique_ptr is now empty - ASSERT_FALSE(robj.isEmpty()); - - // Any subsequent view will still be obtainable - auto view3 = robj.view>(); - ASSERT_TRUE(view3); - - // But the unique_ptr inside is now empty due to the earlier move - std::unique_ptr uptr3 = view3->get(); - ASSERT_TRUE(uptr3 == nullptr); - - // All earlier views now yield empty unique_ptrs as well- no dangling pointers, no UB - std::unique_ptr uptr2 = view2->get(); - ASSERT_TRUE(uptr3 == nullptr); - - std::unique_ptr uptr1 = view1->get(); - ASSERT_TRUE(uptr3 == nullptr); - - // Even reusing the moved-from view0 is safe- just returns empty - std::unique_ptr uptr00 = view0->get(); - ASSERT_TRUE(uptr00 == nullptr); + // RTL still owns it + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); } TEST(RObject_reflecting_unique_ptr, init_with_lvalue) { - constexpr const int NUM = 16238; - std::unique_ptr uptr = std::make_unique(NUM); - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); - // Reflect a move-only type directly into RObject - RObject robj = reflect(std::move(uptr)); - ASSERT_FALSE(robj.isEmpty()); - - // RObject can transparently expose the pointee type - EXPECT_TRUE(robj.canViewAs()); - - auto nodeView = robj.view(); - ASSERT_TRUE(nodeView); - - const Node& node = nodeView->get(); - EXPECT_EQ(node.data(), NUM); - - // RObject can also reflect as the original move-only type - EXPECT_TRUE(robj.canViewAs>()); { - // Multiple independent views to the same stored object- all valid before a move - auto view0 = robj.view>(); - ASSERT_TRUE(view0); - - auto view1 = robj.view>(); - ASSERT_TRUE(view1); + constexpr const int NUM = 16238; + std::unique_ptr uptr = std::make_unique(NUM); + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); + // Reflect a move-only type directly into RObject + RObject robj = reflect(std::move(uptr)); + ASSERT_FALSE(robj.isEmpty()); - auto view2 = robj.view>(); - ASSERT_TRUE(view2); + // RObject can transparently expose the pointee type + EXPECT_TRUE(robj.canViewAs()); - // unique_ptr, ownership managed by RTL. - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); + auto nodeView = robj.view(); + ASSERT_TRUE(nodeView); - // A move from any view transfers ownership of the underlying object - // RObject remains alive and type-consistent, but now holds an empty unique_ptr - std::unique_ptr uptr0 = view0->get(); - ASSERT_TRUE(uptr0); + const Node& node = nodeView->get(); + EXPECT_EQ(node.data(), NUM); - // Access the moved-out value - EXPECT_EQ(uptr0->data(), NUM); + // RObject can also reflect as the original move-only type + EXPECT_TRUE(robj.canViewAs>()); + { + // Multiple independent views to the same stored object- all valid before a move + auto view0 = robj.view>(); + ASSERT_TRUE(view0); - // Verify the original pointer address matches - EXPECT_EQ(uptr0.get(), &node); + auto view1 = robj.view>(); + ASSERT_TRUE(view1); - // RObject still exists with correct metadata, but the stored unique_ptr is now empty - ASSERT_FALSE(robj.isEmpty()); + auto view2 = robj.view>(); + ASSERT_TRUE(view2); - // RTL gave up the ownership after move-op. - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); - // Node still exists. - EXPECT_TRUE(Node::instanceCount() == 1); + // unique_ptr, ownership managed by RTL. + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); - // Any subsequent view will still be obtainable - auto view3 = robj.view>(); - ASSERT_TRUE(view3); + // UPDATE: MOVING DISALLOWED NOW. + // A move from any view transfers ownership of the underlying object + // RObject remains alive and type-consistent, but now holds an empty unique_ptr + const std::unique_ptr& uptr0 = view0->get(); + ASSERT_TRUE(uptr0); - // But the unique_ptr inside is now empty due to the earlier move - std::unique_ptr uptr3 = view3->get(); - ASSERT_TRUE(uptr3 == nullptr); + // Access the moved-out value + EXPECT_EQ(uptr0->data(), NUM); - // All earlier views now yield empty unique_ptrs as well- no dangling pointers, no UB - std::unique_ptr uptr2 = view2->get(); - ASSERT_TRUE(uptr3 == nullptr); + // Verify the original pointer address matches + EXPECT_EQ(uptr0.get(), &node); - std::unique_ptr uptr1 = view1->get(); - ASSERT_TRUE(uptr3 == nullptr); + // RObject still exists with correct metadata, but the stored unique_ptr is now empty + ASSERT_FALSE(robj.isEmpty()); - // Even reusing the moved-from view0 is safe- just returns empty - std::unique_ptr uptr00 = view0->get(); - ASSERT_TRUE(uptr00 == nullptr); + // RTL still owns it + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); + // Node still exists. + EXPECT_TRUE(Node::instanceCount() == 1); + } + EXPECT_TRUE(Node::instanceCount() == 1); + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); } EXPECT_TRUE(Node::instanceCount() == 0); EXPECT_TRUE(Node::assertResourcesReleased()); + ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); } TEST(RObject_reflecting_unique_ptr, destructor_call__reflectecting_lvalue) { - const int NUM = 24028; - std::unique_ptr nodePtr = std::make_unique(NUM); - RObject robj = reflect(nodePtr); - ASSERT_FALSE(robj.isEmpty()); - - // Check if RObject can reflect as `Node` - EXPECT_TRUE(robj.canViewAs()); { - auto view = robj.view(); - ASSERT_TRUE(view); + const int NUM = 24028; + std::unique_ptr nodePtr = std::make_unique(NUM); + RObject robj = reflect(nodePtr); + ASSERT_FALSE(robj.isEmpty()); - const Node& node = view->get(); - EXPECT_EQ(node.data(), NUM); - // Ensure no copy is made for viewing. - EXPECT_TRUE(Node::instanceCount() == 1); - } - // Check if RObject can reflect as `shared_ptr` - EXPECT_FALSE(robj.canViewAs>()); - { - // Get a view of the view as `shared_ptr` - auto view = robj.view>(); - ASSERT_FALSE(view); - } - // Check if RObject can reflect as `unique_ptr` - EXPECT_TRUE(robj.canViewAs>()); - { - // Get a view of the view as `shared_ptr` - auto view = robj.view>(); - EXPECT_TRUE(view); - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() != 0); + // Check if RObject can reflect as `Node` + EXPECT_TRUE(robj.canViewAs()); + { + auto view = robj.view(); + ASSERT_TRUE(view); + + const Node& node = view->get(); + EXPECT_EQ(node.data(), NUM); + // Ensure no copy is made for viewing. + EXPECT_TRUE(Node::instanceCount() == 1); + } + // Check if RObject can reflect as `shared_ptr` + EXPECT_FALSE(robj.canViewAs>()); { - std::unique_ptr movedOutPtr = view->get(); + // Get a view of the view as `shared_ptr` + auto view = robj.view>(); + EXPECT_FALSE(view); + } + // Check if RObject can reflect as `unique_ptr` + EXPECT_TRUE(robj.canViewAs>()); + { + // Get a view of the view as `shared_ptr` + auto view = robj.view>(); + EXPECT_TRUE(view); + EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); + EXPECT_TRUE(Node::instanceCount() == 1); } - EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); - EXPECT_TRUE(Node::instanceCount() == 0); - EXPECT_TRUE(Node::assertResourcesReleased()); } + EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); + EXPECT_TRUE(Node::instanceCount() == 0); + EXPECT_TRUE(Node::assertResourcesReleased()); } TEST(RObject_reflecting_unique_ptr, destructor_call__reflectecting_rvalue) { - const int NUM = 28228; - RObject robj = reflect(std::make_unique(NUM)); - ASSERT_FALSE(robj.isEmpty()); - - // Check if RObject can reflect as `Node` - EXPECT_TRUE(robj.canViewAs()); { - auto view = robj.view(); - ASSERT_TRUE(view); + const int NUM = 28228; + RObject robj = reflect(std::make_unique(NUM)); + ASSERT_FALSE(robj.isEmpty()); - const Node& node = view->get(); - EXPECT_EQ(node.data(), NUM); - // Ensure no copy is made for viewing. - EXPECT_TRUE(Node::instanceCount() == 1); - } - // Check if RObject can reflect as `shared_ptr` - EXPECT_FALSE(robj.canViewAs>()); - { - // Get a view of the view as `shared_ptr` - auto view = robj.view>(); - ASSERT_FALSE(view); - } - // Check if RObject can reflect as `unique_ptr` - EXPECT_TRUE(robj.canViewAs>()); - { - // Get a view of the view as `shared_ptr` - auto view = robj.view>(); - EXPECT_TRUE(view); - ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() != 0); + // Check if RObject can reflect as `Node` + EXPECT_TRUE(robj.canViewAs()); { - std::unique_ptr movedOutPtr = view->get(); + auto view = robj.view(); + ASSERT_TRUE(view); + + const Node& node = view->get(); + EXPECT_EQ(node.data(), NUM); + // Ensure no copy is made for viewing. + EXPECT_TRUE(Node::instanceCount() == 1); + } + // Check if RObject can reflect as `shared_ptr` + EXPECT_FALSE(robj.canViewAs>()); + { + // Get a view of the view as `shared_ptr` + auto view = robj.view>(); + ASSERT_FALSE(view); + } + // Check if RObject can reflect as `unique_ptr` + EXPECT_TRUE(robj.canViewAs>()); + { + // Get a view of the view as `shared_ptr` + auto view = robj.view>(); + EXPECT_TRUE(view); + EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1); + EXPECT_TRUE(Node::instanceCount() == 1); } - EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); - EXPECT_TRUE(Node::instanceCount() == 0); - EXPECT_TRUE(Node::assertResourcesReleased()); } + EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0); + EXPECT_TRUE(Node::instanceCount() == 0); + EXPECT_TRUE(Node::assertResourcesReleased()); } @@ -366,7 +325,8 @@ namespace rtl::unit_test ASSERT_TRUE(view); ASSERT_FALSE(robj.isEmpty()); - std::unique_ptr uptrNode = std::move(view->get()); + // UPDATE: MOVING DISALLOWED NOW. + const std::unique_ptr& uptrNode = view->get(); EXPECT_EQ(uptrNode->data(), NUM); } } diff --git a/ReflectionTemplateLib/rtl/detail/inc/RObjectUPtr.h b/ReflectionTemplateLib/rtl/detail/inc/RObjectUPtr.h index 4e9f72c6..98af8df2 100644 --- a/ReflectionTemplateLib/rtl/detail/inc/RObjectUPtr.h +++ b/ReflectionTemplateLib/rtl/detail/inc/RObjectUPtr.h @@ -82,13 +82,8 @@ namespace rtl::detail return m_uniquePtr.get(); } - std::unique_ptr release() const - { - if (m_uniquePtr) { - RObject::getInstanceCounter()--;//.fetch_sub(1, std::memory_order_relaxed); - return std::move(m_uniquePtr); - } - return nullptr; + const std::unique_ptr& cref() const { + return m_uniquePtr; } private: diff --git a/ReflectionTemplateLib/rtl/inc/view.hpp b/ReflectionTemplateLib/rtl/inc/view.hpp index 552750af..2155f6fc 100644 --- a/ReflectionTemplateLib/rtl/inc/view.hpp +++ b/ReflectionTemplateLib/rtl/inc/view.hpp @@ -44,6 +44,12 @@ namespace rtl const detail::RObjectUPtr& m_uptrRef; + //_asType move() const { + // return std::move(m_uptrRef.release()); + //} + + //friend rtl::RObject; + public: // Construct from reference (no copy, no default init) @@ -55,8 +61,11 @@ namespace rtl view& operator=(view&&) = delete; view& operator=(const view&) = delete; - _asType get() const { - return std::move(m_uptrRef.release()); + // UPDATE: MOVING DISALLOWED NOW. + // cannot move a std::unique_ptr out once its inside rtl::RObject. it can only be viewed. + // done for keeping the rtl::view strictly Read-only and a consistent interface. + const _asType& get() const { + return m_uptrRef.cref(); } }; } \ No newline at end of file