Reputation: 4789
I rewrote my library based on polymorphic memory resources. I think they're amazing, however there are edge cases that are not really covered by the standard.
For example just now I have the following problem: A class type holds an unique_ptr
to a pmr-allocated resource. Now, how to deal with this in the copy constructor? The underlying pmr-resource (a pmr::vector
) will have to be constructed using the pmr-resource (which is passed down automatically using the pmr allocators I think). So I'm using the new_object()
and delete_object()
facilities of the allocator to feed my unique ptr. But it fails because 1) allocators are not delete-functors (don't provide operator()
overload for delete) and/or 2) I can't use a function directly because those functions are non-static. How do I resolve this?
#include <memory_resource>
#include <memory>
struct profile
{
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
profile(allocator_type allocator = {})
: allocator_{allocator}
{}
profile(const profile& other, allocator_type allocator = {})
: allocator_{ allocator }
{
// Deep copy vector (how to do this?)
auto f = std::pmr::polymorphic_allocator<std::byte>::delete_object;
vals_ = std::unique_ptr<profile, decltype(&f)>{ allocator_.new_object<profile>(), f };
}
allocator_type get_allocator() {
return allocator_;
}
allocator_type allocator_;
std::unique_ptr<std::pmr::vector<double>> vals_;
};
int main()
{
profile p;
profile o{p};
}
Error:
<source>:15:62: error: call to non-static member function without an object argument
auto f = std::pmr::polymorphic_allocator<std::byte>::delete_object;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
1 error generated.
Upvotes: 1
Views: 777
Reputation: 126
#include <memory_resource>
#include <memory>
struct profile
{
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
// This is a method
// std::pmr::polymorphic_allocator<std::byte>::delete_object<std::pmr::vector<double>>
// so if you want the unique_ptr to have deleter use this method you must
// also provide it the polymorphic_allocator that it should call this method on
// BUT
// you don't actually even need a unique_ptr to solve the problem
public:
profile(allocator_type allocator = {})
: allocator_{allocator}
{}
profile(const profile& other, allocator_type allocator = {})
: allocator_{ allocator }
, vals_(std::begin(other.vals_), std::end(other.vals_), allocator_)
{
}
allocator_type get_allocator() {
return allocator_;
}
allocator_type allocator_;
std::pmr::vector<double> vals_;
};
int main()
{
profile p;
profile o{p};
}
The unique_ptr was redundant as it was just doing ptr->ptr->actual data Now it is ptr->data. Regarding move/copy construction -> copy construction using the same instance of allocator will behave correctly (std::vector is allocator aware container) same will go for different instance of allocator (second constructor) -> you're delegating the tedious work of implementing Allocator Aware Container (https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer -> https://www.youtube.com/watch?v=kSWfushlvB8) to the actual container in the class -> std::vector Obviously std::move for instances of profile (or even instances of internal vector) would be handled correctly.
If you have a use case that would require that unique_ptr it would become a lot more complicated. unique_ptr has a following template signature:
template<class T, class Deleter>
class unique_ptr{[...]};
It doesn't do any type erasure so every time you would pass around instance of this class it would require you to provide the deleter as a template argument
This is different in case of shared_ptr which has following signature:
template<class T>
class shared_ptr{[...]};
Even though there is no template argument corresponding to deleter it allows usage of them (https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr)
So assuming this use case you would need to create a function like object of known type that would be the the Deleter type argument, however this comes with several disadvantages:
Upvotes: 2