polymorphic_allocator: when and why should I use it? - c++

Here is the documentation on cppreference, here is the working draft.
I must admit that I didn't understand what's the real purpose of polymorphic_allocator and when/why/how I should use it.
As an example, the pmr::vector has the following signature:
namespace pmr {
template <class T>
using vector = std::vector<T, polymorphic_allocator<T>>;
}
What does the polymorphic_allocator offer? What does the std::pmr::vector offer as well in regard of the old-fashioned std::vector?
What can I do now that I wasn't able to do till now?
What's the real purpose of that allocator and when should I use it actually?

Choice quote from cppreference:
This runtime polymorphism allows objects using polymorphic_allocator to behave as if they used different allocator types at run time despite the identical static allocator type
The issue with "regular" allocators is that they change the type of the container. If you want a vector with a specific allocator, you can make use of the Allocator template parameter:
auto my_vector = std::vector<int,my_allocator>();
The problem now is that this vector is not the same type as a vector with a different allocator. You can't pass it to a function which requires a default-allocator vector, for example, or assign two vectors with a different allocator type to the same variable / pointer, eg:
auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error
A polymorphic allocator is a single allocator type with a member that can define the allocator behaviour via dynamic dispatch rather than through the template mechanism. This allows you to have containers which use specific, customised allocation, but which are still of a common type.
The customisation of allocator behavior is done by giving the allocator a std::memory_resource *:
// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);
// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);
auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
// my_vector and my_other_vector have same type
The main remaining issue, as I see it, is that a std::pmr:: container is still not compatible with the equivalent std:: container using the default allocator. You need to make some decisions at the time you design an interface which works with a container:
is it likely that the container passed in may require custom allocation?
if so, should I add a template parameter (to allow for arbitrary allocators) or should I mandate the use of a polymorphic allocator?
A template solution allows for any allocator, including a polymorphic allocator, but has other drawbacks (generated code size, compile time, code must be exposed in header file, potential for further "type contamination" which keeps pushing the problem outward). A polymorphic allocator solution on the other hand dictates that a polymorphic allocator must be used. This precludes using std:: containers which use the default allocator, and might have implications for interfacing with legacy code.
Compared to a regular allocator, a polymorphic allocator does have some minor costs, such as the storage overhead of the memory_resource pointer (which is most likely negligible) and the cost of virtual function dispatch for allocations. The main problem, really, is probably lack of compatibility with legacy code which doesn't use polymorphic allocators.

polymorphic_allocator is to a custom allocator as std::function is to a direct function call.
It simply lets you use an allocator with your container without having to decide, at the point of declaration, which one. So if you have a situation where more than one allocator would be appropriate, you can use polymorphic_allocator.
Maybe you want to hide which allocator is used to simplify your interface, or maybe you want to be able to swap it out for different runtime cases.
First you need code that needs an allocator, then you need to want to be able to swap which one is used, before considering pmr vector.

One drawback of polymorphic allocators is that polymorphic_allocator<T>::pointer is always just T*. That means you can't use them with fancy pointers. If you want to do something like place elements of a vector in shared memory and access them through boost::interprocess::offset_ptrs, you need to use a regular old non-polymorphic allocator for that.
So, although polymorphic allocators let you vary allocation behavior without changing a container's static type, they limit what an allocation is.

Related

Uninitialized memory wrapper

I'm looking for a class that is similar to std::optional, but without the internal flag which tells whether the container is empty or not. I want to be able to declare a variable of type T without invoking T' constructor, and later on move or emplace something into it on my discretion. Specifically I want to work with non-default-constructible T's.
It can be achieved easily with std::optional, but it comes with an overhead of the internal flag. I want this wrapper's size to be equal to sizeof(T).
I know such a class can be implemented using placement new (as are std::optional, std::variant etc). But it looks like a lot of work, and I'm wondering if something like that already exists...
There is nothing for it in the standard library, but it is relatively straight-forward to write such an unsafe optional as a union class. It still requires that you implement the constructor and methods with a placement-new (or construct_at).
However, such a class can't follow the RAII principle properly, because the destructor cannot assume that the unsafe optional is non-empty, so that it can't destroy the contained object. Instead the user of the unsafe optional has to manually choose to destruct the contained object before the unsafe optional's lifetime ends or before a new object is emplaced into it.
It would be preferably to rewrite the user code so that it isn't necessary to construct the empty unsafe optional first. The user code must know whether it contains an object anyway for the reason above, so it should always be possible. (I don't know your concrete use case, so I can't give concrete advice.)
From your comment it seems like you are writing a container. A container can use the standard Allocator concept together with std::allocator_traits as all the standard library allocator-aware containers (e.g. std::vector, std::map, etc.) do:
Your class takes a template parameter called A, usually defaulted to std::allocator<T> (the default allocator using operator new/operator delete), then define
using Alloc = typename std::allocator_traits<A>::template rebind<T>;
and store an instance alloc of Alloc as the allocator (possibly passed through a constructor or default-constructed).
Then to obtain memory you do
T* storage = std::allocator_traits<Alloc>::allocate(alloc, n);
where n is the number of elements to allocate memory for, without constructing any object.
Then to construct the i's object you do
std::allocator_traits<Alloc>::construct(alloc, &storage[i], /*constructor args*/);
To destruct the object you do
std::allocator_traits<Alloc>::destroy(alloc, &storage[i]);
and to deallocate the memory you do
std::allocator_traits<Alloc>::deallocate(alloc, n);
where n must be the same as the allocation size.
That way your container will automatically support all classes as allocator that follow the standard's Allocator concept and no dangerous casts or anything like that is required.

Why is select_on_container_copy_construction needed?

For allocators why is select_on_container_copy_construction needed opposed to just overloading the copy-constructor?
Are there instances when we want to define two seperate copy-construction implementations depending on if we are copying the actual allocator vs container?
(The trait you're referring to is actually called select_on_container_copy_construction.)
The copy constructors of standard library containers are in fact overloaded and provide an allocator-extended version:
A a1 = f(), a2 = g(); // allocators
std::vector<int, A> v1(a1);
std::vector<int, A> v2(v1, a2); // allocator-extended copy
std::vector<int, A> v3 = v1; // regular copy, uses select_on_container_copy_construction
However, using an overload is not always an option, and generally allocator-aware containers should be usable just as easily and seamlessly as if you weren't aware of the allocator choices. That means that certain decisions, such as how to allocate a copy of a container, may need to be customizable directly via the allocator type, and not via the user's type.
For example, you could imagine a situation where the contents of one vector all go onto one (possibly growable) arena, but when you make a new vector, you'd like that to go onto a new, separate arena, and generic code should not need to have to know about this.
Whether this library feature is useful in practice is a separate question, but hopefully this shows why this part design has some motivation.

Difference between allocator supplied as template parameter and allocator supplied as constructor argument in C++ containers?

What's the difference between supplying an STL container (for example, std::vector) with an allocator as a template parameter, eg.:
std::vector<int, std::allocator<int>> some_ints;
and supplying an allocator as a constructor argument, eg:
std::allocator<int> temp;
std::vector<int> some_ints(temp);
and what are the advantages of either, given that they are not the same thing (ie. one supplies a type, the other a type instance) and can be used separately from each other?
Can be used separately from each other?
The template parameter just supplies the type. You still need an instance. It's not separable.
It's like having a function template<typename Type> f(Type instance); and asking what is the difference between Type and instance, can they be used separately and what are the advantages of either. It does not make much sense if you do understand what is a template, type and an instance/object.
(for the sake of simplicity it's c++11)
Here you have type template for vector:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
And here is the default constructor:
explicit vector( const Allocator& alloc = Allocator() );
There always is an instance of Allocator provided as alloc parameter. All other invocation are similar in this regard. By default it is default constructed new Allocator object. So, semantically, whenever you do not use invocation of vector specifying allocator parameter, you do create new Allocator object (which in default case most probably does nothing, but the logical flow of the program is as described).
You cannot pass something that would not fit Allocator because you would get type-mismatch, or precisely in this case a substitution failure.
One pretty non-standard you could do without touching the definition of vector is to define DerivedAllocator which derives from Allocator instantiate it and pass as an argument. So for example:
vector<T> v( DerivedAllocator<T>() );
But I am not able to come up with a use-case for such construction on the top of my head. There is a good use-case, see the addendum below.
What is the Allocator template parameter useful for?
In some system you have more than one type of memory, so it might be useful to provide separate allocators (presicely separate allocator types). E.g: SRamAllocator, RamAllocator, etc.
This is quite common in embedded systems. I know that somewhere there there is a memory model in implementation which actually does not free, when you free it it's a lost chunk. It's essentially a moving pointer. The rationale is that it's extremely fast because it does not have any logic to trace blocks of "holes" caused by freeing. You wouldn't want to use it scenarios with heavy new/delete patterns.
What is the allocator constructor parameter useful for?
It makes sense in case of stateful allocators. Imagine you want to have two storages of the same type. E.g. to track some memory usage, or whatever reason you come with to have more than one logical "memory banks". You may want to create an allocator for each thread in your program, so it's easier to maintain correct CPU/memory affinity.
When you create a new object, you need to tell which of the allocators instances should take care of it.
You could technically implement everything just using different type for each instance, but that would strip down the usability of possible run-time dynamism.
NOTE: Default allocator and pre-c++11 custom allocators are disallowed to have a state, so they basically that to be implemented in a fully static way. It actually does not matter instance of Allocator you use. That is why the default Allocator() works.
So, theoretically one would no need then to instantiate them, and could work with just type and a static interface... if the standard said so. But it was deliberately not made this way to allow allocator types with an internal state (this sentence is a personal opinion).
IMPORTANT ADDENDUM: I've missed one important perk of c'tor parameter allocator, which is quite possibly it's raison d'ĂȘtre. Polymorphic allocators. Is described in detail here: polymorphic_allocator: when and why should I use it?
Basically, using different Allocator type would change the whole type of the object, so one end's up with basically the same object which differ only by allocator. This is under certain circumstances highly undesirable. To avoid it, one can write a polymorphic allocators and use base allocator in the type, and concrete implementations as the runtime parameters. Therefore, one can have object of exactly the same type using different storage engines. So using parameter has some overhead, but it reduces status of the allocator from being iron branded onto the type, to more of an implementational detail.
They actually are exactly the same thing.
In the first example, the vector's default constructor default-constructs an allocator of the type you specified.
In the second, you provided the allocator yourself; it happens to match the default type for the container's allocator.
Both examples make use of default arguments; one is a default function argument, and the other is a default template argument. But the end result in each case is precisely the same.
Here's a demonstrative example:
// N.B. I've included one non-defaulted argument to match
// the vector example, but you could omit `T1` entirely and
// write e.g. `Foo<> obj4`.
template <typename T1, typename T2 = int>
struct Foo
{
Foo(T2 x = 42) : x(x) {}
private:
T2 x;
};
int main()
{
Foo<char, int> obj1; // like your first example
Foo<char> obj2(42); // like your second example
Foo<char> obj3; // this is more common
// obj1, obj2 and obj3 are not only of identical type,
// but also have identical state! You just got there
// in different ways.
}

What is the purpose of the static member functions in STL's allocator_traits?

I am trying to implement an STL-style container class, and I have a question with respect to the use of allocators within my class:
What is the purpose of the static member functions in STL's allocator_traits?
Until now, I thought that I am supposed to instantiate the allocator_type (probably via some kind of empty base optimization to improve the memory footprint). Thus, I would end up with something like this:
struct EmptyBaseOpt : allocator_type
{
EmptyBaseOpt(const allocator_type & a, allocator_type::const_pointer p)
: allocator_type(a), prefix_ptr(p) { }
allocator_type::pointer prefix_ptr;
}
EmptyBaseOpt ebo;
And then, I could use the allocator in the following way:
allocator_type & alloc = ebo;
alloc.allocate(100, ebo.prefix_ptr);
On the other hand, the allocator_traits in C++11 seem to imply the following usage:
std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);
I guess this static allocate member function will probably create a temporary ad-hoc instance of allocator_type via its default constructor. But this leads to the following questions:
What will happen if allocator_type is a stateful allocator? Will such allocators be able to keep their state if I use the static member functions in allocator_traits instead of calling the non-static methods from an instance of allocator_type?
Why should I instantiate allocator_type at all and bother with stuff like EBO if I can directly use the static member functions in allocator_traits instead?
As stated before, my understanding is that any class-like template parameters should be instantiated inside my container class in order to allow for stateful versions of these parameters. Is this understanding correct, and how does it fit with the static member functions in allocator_traits?
On the other hand, the allocator_traits in C++11 seem to imply the following usage:
std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);
No, you're missing the most important argument to that function call: the allocator.
I guess this static allocate member function will probably create a temporary ad-hoc instance of allocator_type via its default constructor.
No, because you pass an allocator argument to the function.
What will happen if allocator_type is a stateful allocator?
It works fine, because you pass that stateful allocator as an argument to the functions that use it.
Why should I instantiate allocator_type at all and bother with stuff like EBO if I can directly use the static member functions in allocator_traits instead?
Because you can't use them instead.
As stated before, my understanding is that any class-like template parameters should be instantiated inside my container class in order to allow for stateful versions of these parameters. Is this understanding correct, and how does it fit with the static member functions in allocator_traits?
Yes, your understanding is correct, and it fits fine with allocator_traits if you use it properly.
The point of allocator_traits is to provide sensible defaults for most of the Allocator interface. This has two purposes:
firstly it is simpler to define an allocator in C++11 (you only need to provide value_type, allocate, deallocate, a template constructor for re-binding the allocator and operator== and operator!=) so writing simple custom allocators now is much simpler.
secondly it allows existing allocators that only meet the C++03 allocator requirements to be used by C++11 containers. C++03 allocators do not define the nested members such as propagate_on_container_swap that C++11 containers look for, or the new variadic construct(pointer, Args&&...) member that allows objects to be constructed with any arguments, not just copy constructed (which is what allows emplace to work). So by wrapping use of allocators in allocator_traits most of the Allocator interface is given sensible defaults, so that existing C++03 code using a custom allocator with a container such as std::vector won't suddenly fail to build if recompiled using C++11. The std::vector implementation only uses the new members through the allocator_traits type and so it doesn't matter that the custom allocator wasn't updated to provide all the new members that are part of the C++11 Allocator requirements.
You seem to have missed the fact that all the static member functions in allocator_traits take Alloc & as the first parameter. That is, they use an object of the allocator type to do their work.
The reason for this policy of "access an allocator through allocator_traits only" is that some members of allocator_traits provide a default implementation of an allocator operation if one is not provided by the allocator type itself. This applies to all members which were added to the allocator requirements in C++11 and were not present in C++03. An example is construct, which will call the placement new operator ::new if the allocator type does not provide a suitable construct function. These defaults thus allow allocators written for C++03 to function unchanged with a C++11 container.
Additionally, using allocator_traits allows for more customisability. allocator_traits can be specified for a particular allocator class and the trait functions can be implemented by different calls on the Alloc & parameter.
So your assumption on instantiating the allocator_type is correct. The difference is that you're not supposed to call its member functions directly (because they might not exist), but through the static accessors in allocator_traits.

Can sizeof(std::list<T>) vary for different types of T?

Can I assume that for any type T, the type std::list<T> will have a same, constant size? Just to make it clear, I mean the size of the 'main' type itself only, not of the memory allocated by it.
It seems logical to me to assume that the size of T itself should only affect the size of list nodes allocated using the allocator.
However, there are two things that might cause the variancy of sizeof(std::list<T>) I can think of:
A standard C++ library trying to 'optimize' the std::list type by putting some of T instances into the std::list<T> itself. That seems like a bad idea to me, and it probably breaks the 'constant time' requirements put by the standard;
A standard C++ having library specializations of std::list<T> for some types, with the specializations having different sizes.
I can't think of any uses for (1) or (2) but I may be wrong.
What I'm thinking of achieving is using a fixed-size slice allocator for a template class usingstd::list<T> inside.
As a note: this is about variance for different types of T, and not for different allocators. I will have an explicit control of all instantiations, and they all will use the std::allocator.
Yes, it can.
The standard does not precise any guarantee on the size of a list<T> object as far as I am aware. Still, there are other guarantees.
Let us debunk some things:
A standard C++ library trying to 'optimize' the std::list type by putting some of T instances into the std::list<T> itself. That seems like a bad idea to me, and it probably breaks the 'constant time' requirements put by the standard;
It does not break the 'constant time' requirement in any way as long as this number is bounded since O(5) is O(1). However, during a splice operations, the nodes should move from one list to another without moving in memory. Thus local storage is prohibited.
A standard C++ having library specializations of std::list<T> for some types, with the specializations having different sizes.
Since we already excluded the possibility of local storage, it is hard to imagine any other variations of the base std::list<T> type by itself.
And let us worry about what matters:
The memory allocated by a std::list<T> is provided by its second template argument: the allocator. Most allocators (including std::allocator<T> which is the default) use a simple pointer type: T*... however an allocator should feel free to change this type.
Therefore, by changing the allocator parameter (and more precisely, its pointer type), one will naturally change the size of the std::list container in all the implementations I have seen of it.
Note that the allocator itself could be specialized for some types, however with the rebind magic it's a bit more difficult to achieve.
Chapter 23 of the C++11 standard (Containers) does not say anything about their sizes, so technically you cannot assume that the size will be constant. Implementors could (in theory) specialize some container for a specific primitive in a way that affects its footprint.
In practice, you could use a static assertion that sizeof(list<T>) == sizeof(list<int>) and use sizeof(list<int>) as the "fixed" size. Worst thing that can happen is a compile-time error.
Fixed-size slice allocator for class using std::list<T> makes little sense unless you also use a fixed-size slice allocator for the list itself there will probably be many more allocations for the list items than of the list headers.
Now you certainly can provide your own fixed-size slice allocator to the lists using their second template argument, the allocator. However there is a twist: You don't know how big the slice should be. A list<T> accepts allocator<T>, but what it really needs is allocator<U> where U is a structure containing the prev/next pointers and T. To get it it calls method rebind, that has signature template <typename T> template <typename T> allocator<U> allocator<T>::rebind<U>().
Since I actually needed to do that once, what I did was to create an object, that holds collection of allocators for several slice sizes. The allocator itself than held a shared pointer to this and a shared pointer to the allocator for size of it's argument. And on rebind I pulled appropriate allocator from the collection, or created one if it didn't exist. If you do this, it will as a side-effect solve allocation of the lists, because if there is more than one size, it will create separate pool for each. And it's not like there would be many different sizes, because the objects won't be big.