Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion modules/strong_ptr.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ struct monotonic_allocator_base : public std::pmr::memory_resource
*
* @tparam MemorySize number of bytes in the internal storage buffer
*/
template<size_t MemorySize>
export template<size_t MemorySize>
struct monotonic_allocator
{
/// @brief Initializes the internal buffer and sets up the base allocator
Expand Down Expand Up @@ -336,6 +336,7 @@ struct rc
return sizeof(rc<T>);
}
};

// Check if a type is an array or std::array
template<typename T>
struct is_array_like : std::false_type
Expand Down Expand Up @@ -1024,6 +1025,25 @@ export template<class T>
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
*
Expand Down Expand Up @@ -2117,4 +2137,12 @@ export template<class T, typename... Args>

return result;
}

export template<class T, typename... Args>
[[nodiscard]] constexpr strong_ptr<T> make_strong_ptr(
std::pmr::polymorphic_allocator<> p_memory_resource,
Args&&... p_args)
{
return make_strong_ptr<T>(p_memory_resource.resource(), p_args...);
}
} // namespace mem::inline v1
31 changes: 31 additions & 0 deletions tests/strong_ptr.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<test_class>(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)
}

Expand Down
5 changes: 5 additions & 0 deletions tests/util.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ export {
return weak_from_this();
}

std::pmr::memory_resource* get_memory_resource()
{
return memory_resource();
}

private:
int m_value;
};
Expand Down
Loading