How to use an allocator for a hierarchically allocated class - c++

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.

Related

How to rebind a custom allocator?

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.

Why does the STL reserve an interface for Allocator?

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.

Returning vectors that use custom allocators

Suppose you developed an optimized custom allocator that you want to use with std::vector (for example, for small allocations the custom allocator gets memory from the stack instead of the heap, kind of like std::string's SSO; or it allocates big chunks of memory using e.g. VirtualAlloc on Windows, and then single allocations are carved from inside a chunk just increasing a pointer).
typedef std::vector<T, OptimizedAllocator<T>> OptimizedVector;
How to use it in a context where you are returning a vector from a function, like this?
OptimizedVector DoSomething()
{
OptimizedVector<int>::allocator_type alloc{};
OptimizedVector<int> v{alloc};
// Do stuff...
return v;
}
At the end of the function's scope, the allocator object is destroyed, so the returned vector could contain garbage, right?
An alternative might be to pass the allocator object as a reference to each function as an additional parameter, but that's kind of ugly...
Is there a way to safely "embed" the allocator object in the vector?
How do you solve this problem?
An allocator must be CopyConstructible without exceptions, and afterwards a1 == a2 must hold (meaning they share the same memory pool). I strongly suggest reading this page on the Allocator concept.
std::vector stores a copy of the allocator you give it.
So if you properly implement your allocator to be safe to copy, there will be no problem with your given code.
The allocator is kept by copy as defined by the standard:
n4140 §23.2.1 [container.requirements.general]/8
Unless otherwise specified, all containers defined in this clause
obtain memory using an allocator [...] A copy of this allocator is
used for any memory allocation performed [...]
vector is not "otherwise specified".
This also means that your allocator should support copying and probably be a handle for the real allocator if it needs state.
From here:
The container keeps an internal copy of alloc, which is used to allocate and deallocate storage for its elements, and to construct and destroy them (as specified by its allocator_traits).
So, if I'm not wrong, what you're asking for is the default.

Placement new with std::list

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.

what does allocator mean in STL

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.