Why does the STL reserve an interface for Allocator?
Taking vector as example:
template<class T,class Allocator = std::allocator<T>>
class vector;
Since there are many options for us to allocate memory and construct objects like
operator new,delete,new[],delete[],which could do almost anything we need when we create an object.
So why does the STL containers like vector need an Allocator interface,which at most time is the default std::allocator if we don't assgin one? Why not just use the new expressions?
If the purpose is to make user-defined allocating behaviors possible,why not just let the users provide their self-defined operator new,new[],etc?
So specific vector and change how it allocates without changing how all vectors allocate.
new must be overridden on a per-type basis of what you are allocating, or replaced globally. std::vector<int> and std::vector<int, some_other_allocator> can use a different allocation strategy; in fact, the allocator can be stateful and have shared state.
A classic one is a fast stack allocator that doesn't deallocate until it goes out of scope.
For example std::list<T, Allocator> also provides this interface. Usually you are told not to use linked lists because of bad cache-locality.
If you still need to use a linked list since for example it never invalidates iterators you can optimize std::list by using a pool allocator
#include <boost/pool/pool_alloc.hpp>
template <typename T>
using pooled_list = std::list<T, boost::pool_allocator<T>>
This way you can optimize your code without re-implementing std::list. This is an example of policy based design. Another example would be std::char_traits for std::basic_string.
Related
I need a custom allocator for STL vectors and maps so it "allocates" memory in preallocated memory block. I came across this piece of code which is in almost every allocator out there.
Allocator(const Allocator<U, growSize> &other)
{
if (!std::is_same<T, U>::value)
rebindAllocator = new std::allocator<T>();
}
Can somebody please explain what it does and why we need this interface to be implemented?
Big thanks in advance.
Full source code
Think of an allocator as a typed interface referencing some underlying untyped storage. Many allocators may reference the same storage.
Allocators are required to allow rebinding so that an allocator for T can be turned into an allocator for U referencing the same storage. Allocators are required to be copy constructible, and a copy of the allocator must reference the same storage. See cppreference.
This allocator implementation also is the storage. It derives from a memory pool. Therefore, it needs a way for a copied allocator to allocate and deallocate from the original storage, not its own. This is copyAllocator. For reasons which are not clear, it only does this on Windows.
Similarly, a rebound allocator needs to access the same storage. This allocator seems to violate that and use std::allocator. This means it is not fit for many STL use cases. std::map and other node-based containers will allocate with a rebound allocator, which means they won't use the memory pool.
Given a raw array of elements, how to create a std::vector that takes ownership of the raw array without reallocate & copy?
For example having the raw array:
int* elems = new int[33]
how to create a std::vector of size 33 pointing to elems?
I am sure that theoretically this is possible, as usually std::vector is implemented as a structure containing three pointers, one pointing to the beginning of the allocated memory, one to the end of the valid elements, and one to the end of the allocated memory. But is there a standard way of initializing the std::vector structure with the raw array?
What you need is a "view" rather than a container. Containers own their elements and their main purpose is to encapsulate the raw memory they manage. If you need to manage the memory yourself then you dont need a container. Take a look at string_view that would be your solution if you had a string. Perhaps boost ranges is something that you could apply. From the docs (emphasize mine):
The motivation for the Range concept is that there are many useful
Container-like types that do not meet the full requirements of
Container, and many algorithms that can be written with this reduced
set of requirements. In particular, a Range does not necessarily
own the elements that can be accessed through it,
have copy semantics,
PS: Actually std::array_view was considered for C++17, but unfortunately it didnt make it into the standard.
The reason why this is not directly possible is that the standard library uses allocators to reserve memory for the containers.
Therefore if you have an std::vector which uses a certain type of allocator and give it some pointer you have created, you effectively break the allocator idiom. If your implementation of the standard library for example uses malloc and free instead of new and delete, your program will fail.
For this to become a standard way, the standard library would need to provide a constructor that accepts a T* which additionally must have been returned by the same allocator the vector uses later. So the signature of the constructor you need would be something like std::vector::vector(T* data, size_type used_elements, size_type capacity, const Allocator& alloc). Notice that the allocator argument is necessary as the T* must (theoretically) have been returned by the exact same allocator that is used in the vector.
You can achieve some of the functionality this by creating your own allocator according to this concept, but to have your 33 elements to not be reconstructed you will also have to provide a allocator::construct(..) function which is a no-op until the 34th element (exclusive). Additionally you will have to initially resize your vector to 33 elements to force the vector to have the correct size.
That being said this nevertheless is a bad idea because for the conditional construct and allocate functions you will probably have more overhead opposed to copying your elements once.
In accordance to this, there is no constructor that accepts pointer to data. So, you can't pass the ownership over raw array to vector.
You can only create a vector and place data into it.
If the type of the object you are working on is movable, you can do this:
template<typename T>
std::vector<std::unique_ptr<T>> ConvertArrayToVector(T* data, size_t size)
{
std::vector<std::unique_ptr<T>> result(size);
for (unsigned int i = 0; i<size; ++i)
result[i] = std::make_unique<T>(std::forward<T>(data[i]));
return result;
}
The resulting vector owns the array now, in a sense that it stores pointers to its elements and makes sure the objects are deleted when the vector is destroyed, but the original array gets invalidated in the process.
Given a raw array of elements, how to create a std::vector that
takes ownership of the raw array without reallocate & copy?
There is no way.
how to create a std::vector of size 33 pointing to elems?
Impossible.
I am sure that theoretically this is possible,
No, it isn't.
But is there a standard way of initializing the std::vector structure with the raw array?
No.
That being said, chances are that you may be able to hack together a solution with a custom allocator. However, in addition to the fact that writing custom allocators is a rarely-used error-prone technique, you shouldn't overestimate the usability of such a solution.
std::vector<int> and std::vector<int, MyAllocator> are two different classes. If your goal is to interface with code that expects std::vector<int>, then std::vector<int, MyAllocator> cannot be used; and if you intend to create and use std::vector<int, MyAllocator> in your code, then honestly you'd be better off just implementing your own non-owning container class, i.e. something like a custom VectorView<T>.
Suppose I have a std::vector<std::string>.
Even if I specify a custom allocator for the vector, my std::string-s will still use the standard string allocator.
Can I use custom allocator to be shared for both the container and the containees?
If you have an allocator that you want to use with a hierarchical container, the standard library provides a solution: scoped_allocator_adaptor. When you use the adapter, it forces that allocator to be passed downwards to any allocator aware container. This requires that the container correctly specializes the allocator aware trait, and that all of its constructors have an overload that takes an allocator last. Here's the example of usage from http://en.cppreference.com/w/cpp/memory/scoped_allocator_adaptor:
namespace bi = boost::interprocess;
template<class T> using alloc = bi::adaptive_pool<T,
bi::managed_shared_memory::segment_manager>;
using ipc_row = std::vector<int, alloc<int>>;
using ipc_matrix = std::vector<ipc_row, std::scoped_allocator_adaptor<alloc<ipc_row>>>;
One thing to note is that allocators are of course part of the type of the container. So this doesn't get you out of needing to specify the correct allocator type for the inner container. What this does is ensure the allocator instance gets passed down. This is important for allocators which are not stateless. The example continues:
bi::managed_shared_memory s(bi::create_only, "Demo", 65536);
// create vector of vectors in shared memory
ipc_matrix v(s.get_segment_manager());
As you might get this allocator is not stateless.
If you have a stateless allocator you don't need to deal with any of this, you would just define the type of the outer and inner containers to use the same allocator and that would be that.
I won't get into it here, but another approach is to use the new pmr approach to allocators. It hasn't been merged into the standard. It does make things quite a bit simpler because everything is type-erased and I believe it passes down to nested containers automatically. You can find a library implementation of it somewhere if you google.
Suppose I have std::vector of std::string. How can I use custom allocator for it? I can do allocator for the vector, but then I need to do for strings as well?
When you use a custom allocator for a container, you command the container to allocate memory using your allocator.
A container cannot be responsible for any allocations performed by the Object it contains, so yes, you will have to use std::basic_string with a custom allocator as well.
Suppose I have own class, similar to linked list of std::string. How can I use custom allocator for it? Shall I do my own implementation or say replace malloc with xmalloc etc.
Again, a container should NOT be responsible/aware of allocations performed by its containees! Instead, use std::basic_string with a custom allocator.
scoped_allocator_adaptor
Regarding the updated question, as Nir Friedman's answer suggests, scoped_allocator_adaptor is a standard solution for using the same allocator for a container and its containees.
This doesn't mean that the container is responsible for the allocations of the containee, but rather that they both share the same allocator.
I am looking to implement a (doubly) linked list which only calls placement new internally, directing all memory to a pool allocated with something like:
char *memPool = new char[4096]; // One-off normal 'new'
Initially I was going to implement my own class which takes a pointer to a (class managing a) pre-allocated memory pool. However I want to be sure first that I can't achieve the same outcome with std::list. In particular, the third section of David RodrÃguez's answer to this SO question worries me.
It makes sense that std::list will have to call new and delete on its component nodes, but I want to modify this behaviour so that all the nodes to be allocated with placement new into my custom pool. Therefore my question is:
Is there a way to specify that a placement new std::list such as:
std::list<std::shared_ptr<Cls>> myList = new (pool.getFreeAddr()) list<Cls>;
should also allocate its nodes using a custom allocator, so that everything is stored strictly inside my own memory pool?
(Note: I am aware of the need to use custom allocation/deletion functions with the shared_ptrs if I want them in the custom memory pool too.)
You have to:
Write a class template MyAllocator that satisfies the Allocator requirements ([allocator.requirements] in the standard).
Use a std::list<T, MyAllocator<T> > in place of a std::list<T>.
If you need the type of your list to be specifically std::list<T> (for example because you want to call functions that accept a std::list<T> & and whose interface you can't change), then you're out of luck because the allocator type is part of the container type.
Be careful about the allocator requirements, they're strange. In particular you will need rebind for a list, and it's a bit tricksy.
Furthermore, in C++03 there's no guarantee that allocator instances are respected, only the allocator type, which in effect means that the pointer to the pool you're allocating from needs to be stored with static duration rather than as an instance variable. I think that was changed in C++11, but I might be wrong. It only really matters if you want to use multiple different pools in your program.
I'm using list class in c++ and i don't know what does allocator mean here
template < class T, class Allocator = allocator<T> > class list;
if i have list <int> mylist would it mean allocate integer type of memory using the allocator when an element is added to list? and when do you want a custom allocator?
Yes. An allocator is a way of factoring the allocation of memory from the use of memory. If a container needs some memory, instead of:
// too rigid, cannot allow custom allocation schemes
void* mem = ::operator new(someAmount);
You get:
// flexible, allows custom allocation schemes
void* mem = myallocator.allocate(someAmount);
There is a standard allocator, std::allocator, which uses global operator new and operator delete.
You want to use your own allocator anytime you need to allocate in a special way. These cases may be: get memory from some freelist, allocate from the stack, etc. (Generally for optimization purposes, though you could also record statistics with a custom allocator) Most of the time, the standard allocator works perfectly.
In addition to what GMan pointed out, custom allocators can be used for aligned memory allocation. Some high performance libraries which use SIMD instruction set require aligned memory. Those libraries may provide you with ready to use allocators which you can plug into any STL container. There are also cache aligned allocators to avoid false sharing in multi-threaded code. Intel's TBB comes with scalable allocators, which can speed up multi-threaded memory allocation/delocation.