diff --git a/modules/strong_ptr.cppm b/modules/strong_ptr.cppm index c92bec0..a125b5c 100644 --- a/modules/strong_ptr.cppm +++ b/modules/strong_ptr.cppm @@ -116,7 +116,7 @@ struct monotonic_allocator_base : public std::pmr::memory_resource * * @tparam MemorySize number of bytes in the internal storage buffer */ -template +export template struct monotonic_allocator { /// @brief Initializes the internal buffer and sets up the base allocator @@ -336,6 +336,7 @@ struct rc return sizeof(rc); } }; + // Check if a type is an array or std::array template struct is_array_like : std::false_type @@ -1024,6 +1025,25 @@ export template class enable_strong_from_this { public: + /** + * @brief Returns the address of the object's memory resource + * + * This allows objects using `enable_strong_from_this` access to the memory + * resource used to construct it. This can be useful for the PIMPL idiom of a + * private implementation. At construction the memory resource can be used to + * allocate the members of the object. When used with memory resources that + * allocate in a single direction (monotonic memory resources), then the + * memory for the objects will be located right next to the object itself, + * maximizing memory locality. + * + * @return std::pmr::memory_resource* - get the allocator used by this + * Returns `nullptr` if the the object is statically allocated. + */ + [[nodiscard]] constexpr std::pmr::memory_resource* memory_resource() + { + return m_ref_counted_self->m_info.allocator; + } + /** * @brief Get a strong_ptr to this object * @@ -2117,4 +2137,12 @@ export template return result; } + +export template +[[nodiscard]] constexpr strong_ptr make_strong_ptr( + std::pmr::polymorphic_allocator<> p_memory_resource, + Args&&... p_args) +{ + return make_strong_ptr(p_memory_resource.resource(), p_args...); +} } // namespace mem::inline v1 diff --git a/tests/strong_ptr.test.cpp b/tests/strong_ptr.test.cpp index 4c5c378..95f57ec 100644 --- a/tests/strong_ptr.test.cpp +++ b/tests/strong_ptr.test.cpp @@ -417,6 +417,37 @@ void run_test() noexcept expect(derived.get_allocator() == base.get_allocator()) << "Derived and base pointers should share the same allocator"; }; + + "make_strong_ptr_with_polymorphic_allocator"_test = [&] { + // Create a polymorphic allocator from the test allocator + std::pmr::polymorphic_allocator<> pmr_alloc(test_allocator); + + // Create a strong_ptr using the polymorphic allocator overload + auto ptr = make_strong_ptr(pmr_alloc, 42); + + // Verify the object was created correctly + expect(that % 42 == ptr->value()) + << "Object should be created with correct value"; + + expect(that % 1 == test_class::instance_count) + << "Should have created one instance"; + + expect(that % 1 == ptr.use_count()) << "Should have exactly one reference"; + + // Verify the allocator is stored correctly + expect(that % test_allocator == ptr.get_allocator()) + << "Allocator should match the original allocator"; + + // Test copy behavior with polymorphic allocator + auto ptr2 = ptr; + expect(that % 2 == ptr.use_count()) + << "Should have two references after copy"; + + // Modify through the copy + ptr2->set_value(100); + expect(that % 100 == ptr->value()) + << "Modification through copy should affect original"; + }; // NOLINTEND(performance-unnecessary-copy-initialization) } diff --git a/tests/util.cppm b/tests/util.cppm index d870cd3..a2e3599 100644 --- a/tests/util.cppm +++ b/tests/util.cppm @@ -144,6 +144,11 @@ export { return weak_from_this(); } + std::pmr::memory_resource* get_memory_resource() + { + return memory_resource(); + } + private: int m_value; };