Headers:
<memory_resource>
<memory>
Tools:
std::allocator_traits , use as std::allocator_traits<allocator<T>>
std::is_empty , test if a type can be used with [no_unique_address] (reference: http://vsdmars.blogspot.com/2018/05/caccu2018-tricks-library-implementation.html)
Concept (must read):
C++ concepts: Allocator
excerpt from Howard Hinnant' Allocator Boilerplate
- Allocator must be CopyConstructible and MoveConstructible
There's no Move Constructor, please refer to:
std::allocator::allocator
std::pmr::polymorphic_allocator::polymorphic_allocator
The concept of Allocator mentioned clearly:
A a1(std::move(a))
A a1 = std::move(a)
A a(std::move(b))
The value of a is unchanged and a1 == a. (since C++20)
Allocator must be "Copy-Only" types.
Why?
Consider:
vector<int, Allocator<int>> v1;
vector<int, Allocator<int>> v2 = std::move(v1);
// There's no allocator, it's been moved!
v1.clear(); v1.push_back(42);
Refer to: LWG issue 2593 - If propagate_on_container_copy_assignment{} is true, the allocator must be CopyAssignable.
- If propagate_on_container_move_assignment{} is true, the allocator must be MoveAssignable.
- If propagate_on_container_swap{} is true, the allocator must be Swappable.
- If they exist, these operations should not propagate an exception out. However they do not need to be marked with noexcept.
- Recommend mark them with noexcept if the compiler does not implicitly do so, so that traits such as is_nothrow_copy_constructible<allocator<T>> give the right answer.
Effective STL, Item 10, 11
Reference:
http://vsdmars.blogspot.com/2017/10/c-allocator-template-code.html
Back to the main topic:
std::pmr i.e <memory_resource>std::pmr::polymorphic_allocator (value type)
By skimming through the
polymorphic_allocator constructor signature, there's no move constructor, indicates that, it shares resources.
Unlike stateless allocator, polymorphic_allocator doesn't have 'state', but value, which is the value of pointer point to the shared resource. It has state, but immutable state, which is that the pointer to resource is const.
'allocate'/'deallocate' can be marked as 'const', which the data member pointer point's to the shared resource shouldn't be changed.
That is to say, polymorphic_allocator is a Regular type.
Be ware about who owns the memory resource(std::pmr::memory_resource, NOT a value type)?
We can call std::pmr::get_default_resource to get the underneath memory_resource ptr.
--------------------------------------------------------
For stateless allocator:
An allocator object represents a source of memory.
Now for valued allocator:
An allocator value(aka. ptr to memory_resource) represents a handle to a source of memory.
e.g (hell, simple. Now we can even _not_ consider making UDT destructor virtual, deleting destructor issues etc.)
class My_Resource : public memory_resource{ // non-const since change states void *do_allocate(size_t bytes, size_t align) override { return ::operator new(bytes, std::align_val_t(align)); } // non-const since change states void do_deallocate(void *p, size_t bytes, size_t align) override { ::operator delete(p, bytes, std::align_val_t(align)); } bool do_is_equal(const memory_resource& rhs) const noexcept override { return (this == &rhs); // God, no dynamic_cast for '==', never! } }; inline memory_resource *get_my_resource() noexcept { static My_Resource instance; // thread safe return &instance; }
std::pmr::polymorphic_allocator internally will store the value of a pointer point to memory_resource, which is a word(64 bits) in 64bit machine, which means there are as much as 2^64 distinct memory_resource can be used.
Why std::pmr::polymorphic_allocator taking memory_resource as pointer instead of reference?
Be careful that if passing a local resource by reference, it dangerous. It's hard to see inside the callee that the reference it's lifetime/ownership. However, if passing as pointer, the engineer should know to be aware that it's lifetime/ownership.
Getting the traditional ::operator new/delete memory_resource:
std::pmr::new_delete_resource
Thus, can we make our own stateless std::pmr::polymorphic_allocator?
Of course! Just by default using std::pmr::new_delete_resource as std::memory_resource.
Excerpt from the talk sub-sum up:
- Allocator types should be copyable, just like pointers.
- Always true but in C++17 it's more obvious.
- Allocator types should be cheaply copyable, like pointers.
- Not necessarily trivially copyable.
- Memory_resource types should generally be immobile(with the same virtual memory address)
- A memory resource might allocate chunks out of a buffer stored inside itself as a data member.
-------
std::pmr::synchronized_pool_resource Alike Herb Sutter's talk in Cpp 2015? A centralized heap memory management.
std::pmr::unsynchronized_pool_resource Unthread safe version of std::pmr::synchronized_pool_resource
-------
Allocators are 'rebindable family' types.
Why? From Effective STL, Item 10, due to associate containers that the type instance stored is not the 'type instance' but internal data structure inside the container.
i.e
Containers take this type inside:
rebind_alloc<T> from std::allocator_traits
std::array does not use allocator, it's allocated on stack.
std::vector is the only sequential container which doesn't use rebind.
---------
Other 'rebindable families' in C++:
std::pmr::synchronized_pool_resource Alike Herb Sutter's talk in Cpp 2015? A centralized heap memory management.
std::pmr::unsynchronized_pool_resource Unthread safe version of std::pmr::synchronized_pool_resource
-------
Allocators are 'rebindable family' types.
Why? From Effective STL, Item 10, due to associate containers that the type instance stored is not the 'type instance' but internal data structure inside the container.
i.e
Containers take this type inside:
rebind_alloc<T> from std::allocator_traits
std::array does not use allocator, it's allocated on stack.
std::vector is the only sequential container which doesn't use rebind.
---------
Other 'rebindable families' in C++:
- Allocator types
allocator_traits<Alloc<T>>::rebind_alloc<U> == Alloc<U> - Pointer types
pointer_traits<Ptr<T>>:rebind<U> == Ptr<U> - Smart-pointer types
decltype(reinterpret_pointer_cast<U>(Sptr<T>{})) == Sptr<U> - Promise and future types
decltype(future<T>{}.then([](auto&&... u) -> U {throw;})) == future<U>
Each 'rebindable family' has a prototype:
- Pointer and smart-pointer families have a 'void pointer' type
- Ptr<void>, Sptr<void>
- Allocator families have a 'proto-allocator' type
- Alloc<void>
- Promise and future types have a 'future of void' type
- future<void>
Fancy pointer:
i.e
std::allocator_traits::allocate could return a pointer points to either:
- T*
- s.t fancier, like boost::interprocess::offset_ptr
Reference:
[reddit] Q: How should C++ support persistent memory?
http://vsdmars.blogspot.com/2018/06/cproposalnote-implicit-creation-of.html
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.