How to rebind a custom allocator? - c++

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.

Related

How to use an allocator for a hierarchically allocated class

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.

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.

Could STL containers be used without being destroyed if the allocator's deallocate() is noop?

Sorry for the long title~
I have a simple allocator implemented on top of a memory pool whose deallocate() is empty. If a STL container is used with this allocator and the elements of the container are trivially destructible, can I avoid invoke the container's destructor altogether ?
The standard probably says no, I'm not sure though. IMO, if this behavior is allowed, STL containers couldn't get any resources other than memory allocated from their allocators, which would limit STL vendors too much. I'm never an expert on the standard, I'd be glad to be proven wrong.
However, if my simple allocator is used along with some actual implementation like libstdc++ or libc++, would the program leak memory or run into other problems ?

STL Containers, SBO and custom allocator conflicts

I've recently been fiddling with developing a custom allocator based on a memory pool, that is shared between multiple instances of the allocator.
The intention was that the allocator be compatible with STL and Standard C++ based containers such as vector, deque, map, string etc
However something in particular has caused me some confusion. Various implementations of the containers such as std::vector, std::string make use of Small Buffer Optimisation - stack based allocation for small initial memory requirements.
For example MSVC9.1 has the following member in the basic_string class:
union _Bxty
{ // storage for small buffer or pointer to larger one
_Elem _Buf[_BUF_SIZE];
_Elem *_Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
I can't see how when instantiating such containers one can cajole
the implementation to only and always use the provided allocator
and not use SBO. I ask because one of intentions of implementing
custom allocators was to be able to use them in a shared memory
context, where the amount of the shared memory may be less than
the SBO limit some of the various implementations may use.
For example I would like to have a situation where I can have two
instances of std::string one per process sharing a common block
of memory which maybe smaller than or equal to the SBO upper
limit.
Possibly related: May std::vector make use of small buffer optimization?
typedef std::vector<int,mysharedmemallocator> shmvtype;
shmvtype v(2,0); //<-- if SBO then error as memory is allocated on
//stack not via the allocator
v[1] = 1234; //<-- if SBO then error as wrong piece of memory
// is being modified.
Lets look at another example that is not based on shared memory as it seems to over complicate things for some people. Lets say I want to specialize my std::basic_string or std::vector etc with an allocator that fills the memory it allocates with the value 0xAB prior to presenting the pointer back to the calling entity for no reason other than whimsy.
A container that is specialised with this new allocator, but that also uses SBO, will not have its SBO based memory filled with 0xAB pattern. So for example:
typedef std::basic_string<char,myfillmemallocator> stype
stype s;
s.resize(2);
assert(s[0] == 0xAB); // if SBO this will fail.
one of intentions of implementing custom allocators was to be able to use them in a shared memory context
This may be what you intend to do with it, but that's not why they exist. Indeed, with the exception of basic_string in C++98/03, it is not legal to share allocated memory between objects at all. They can share allocator objects, so they can get their memory from the same place. But it is illegal for modifications of one object to impact another that is unrelated; each instance must be separate.
Copy-on-write strings only work because the system assumes that any non-const access to a character will write to it, thus performing a copy. And in C++11, even basic_string is forbidden from doing copy-on-write-style stuff like this.
For example I would like to have a situation where I can have two instances of std::string one per process sharing a common block of memory which maybe smaller than or equal to the SBO upper limit.
That's not possible without writing your own class. The allocator only controls where the memory comes from. What you're wanting is a guaranteed copy-on-write string or some sort of shared string class.
What you want requires a container class specifically designed for this purpose.

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.