Polymorphic Allocators in C++17
This post is a cross-post from www.ModernesCpp.com.
This post starts a miniseries about an almost unknown feature in C++17: polymorphic allocators. I often promised that I would write about polymorphic allocators. Today, I fulfill my promise.
Since C++98, you can fine-tune memory allocation in general but also for user-defined types or containers of the standard library. For example, the containers of the sequential and associative containers of the STL have a defaulted allocator parameter. For example, here are the containers std::string, std::vector, and std::unordered_map:
template<class CharT, class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>>
class basic_string;
using string = basic_string<char>;
template<class T, class Allocator = std::allocator<T>>
class vector;
template<class Key, class T, class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>>
class unordered_map;
The memory is allocated from the heap by default, but this default is not always appropriate. Often, you want to apply another strategy. Here are a few ideas.
With C++98, you could implement and use your memory allocator, but this was pretty challenging and error-prone. I have already written two posts about memory allocators in C++: “Memory Management with std::allocator” and a guest post from Jonathan Müller “Memory Pool Allocators“.
Thanks to polymorphic allocators in C++17, your job becomes way easier. Before I dive into the details, let me give you an introductory example.
// polymorphicAllocator.cpp
#include <array>
#include <cstddef>
#include <memory_resource>
#include <vector>
int main() {
std::array<std::byte, 200> buf1; // (1)
std::pmr::monotonic_buffer_resource pool1{buf1.data(), buf1.size()};
std::pmr::vector<int> myVec1{&pool1}; // (3)
for (int i = 0; i < 5; ++i) {
myVec1.push_back(i);
}
char buf2[200] = {}; // (2)
std::pmr::monotonic_buffer_resource pool2{std::data(buf2), std::size(buf2)};
std::pmr::vector<int> myVec2{&pool2};
for (int i = 0; i < 200; ++i) {
myVec2.push_back(i);
}
}
First, I allocate memory on the stack. I can use a byte array (line 1) or a char array (line 2) for that job. Furthermore, I initialize a std:pmr::motonotic_buffer with the address and the size of the already stack-allocated memory. The final step is that the std::pmr::vector takes this memory resource.
The namespace component pmr stands for a polymorphic memory resource. All components of the polymorphic allocators are in this namespace. The containers of the STL have their pmr pendants. This pmr pendant is an alias for a std::vector using the special allocator. The following two lines are equivalent:
std::pmr::vector<int> myVec1{&pool1};
std::vector<int, std::pmr::polymorphic_allocator<int>> myVec1{&pool1};
When you study the example polymorphicAllocator.cpp, you may wonder: How could a vector of 200 int‘s myVec2 fit into a char array of 200 elements? The answer is simple: std::pmr::new_delete_resource() as the so-called upstream allocator kicks in as a fallback. C++17 provides a few predefined memory resources.
?
Modernes C++ Mentoring
Be part of my mentoring programs:
Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.
?
Predefined Memory Resources
I will apply the listed predefined memory resources in upcoming posts. Therefore, here is a short overview. All predefined memory resources are derived from the interface class std::pmr::memory_resource. The class provides three public member functions allocate, deallocate, and is_equal.
Correspondingly, std:pmr::memory_resource has three private virtual member functions do_allocate, do_deallocate, and do_is_equal. Typically, a user-defined memory resource overwrites these three virtual member functions, as do the predefined memory resources:
std::pmr::new_delete_resource
Returns a pointer to a memory resource, calling the global new and delete. It is the default memory resource if not otherwise specified.
std::pmr::null_memory_resource
Returns a pointer to a “null” memory resource. Using this memory resource for allocating causes a std::bad_alloc exception. This memory resource ensures you do not arbitrarily allocate memory on the heap.
领英推荐
std::pmr::synchronized_pool_resource
A class that creates a memory resource with less fragmentation that is thread-safe. This class acts as a wrapper around the default resource.
std::pmr::unsynchronized_pool_resource
A class that creates a memory resource with less fragmentation that is not thread-safe. This class acts as a wrapper around the default resource.
std::pmr::monotonic_buffer_resource
A class to create memory resources in a big chunk of memory that is not thread-safe. You can optionally pass it as a buffer. This memory resource is pretty fast and can only grow. It destroys the objects but does not free the memory.
I used in the previous program polymorphicAllocator.cpp a std::pmr::monotonic_buffer with a byte-array (line 1) and a char-array (line 2) as a passed buffer. Additionally, the vector myVec2 allocated the remaining memory with the default allocator (upstream allocator) std::pmr::new_delete_resource.
What’s Next?
Admittedly, his was a technical post. In my next post, I will apply the theory and implement a tracking memory resource. This user-defined memory resource will track all allocations and deallocations of the upstream allocator.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dr?ge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschl?ger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, and Bhavith C Achar.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero, PVS-Studio, Tipi.build?,and Take Up Code.
Seminars
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Bookable
German
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
New
Contact Me
Modernes C++ Mentoring,