Special Allocators with C++17

Special Allocators with C++17

This post is a cross-post from www.ModernesCpp.com.

I introduced in my last post “Polymorphic Allocators with C++17” the theory of polymorphic allocators in C++17. Today, I will apply the theory.

Before I go on, here are the essential parts of my last post: “Polymorphic Allocators with C++17”.

A Short Reminder

The following program uses polymorphic allocators.

// 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);
    }

}
        

Now, I want to focus on myVec2. It pushes 200 ints onto the std::pmr::vector<int>. 200 ints do not fit into a char buf[200] and, therefore, std::pmr::new_delete_resource() as the so-called upstream allocator kicks in and calls the global new for the remaining elements. Let me instrumentalize the upstream allocator.

A Tracking Allocator

The following program is based on the previous one, uses a tracking allocator, and makes the dynamic memory allocation and deallocation visible.

// trackAllocator.cpp

#include <array>
#include <cstdlib>
#include <format>
#include <iostream>
#include <memory_resource>
#include <vector>

class TrackAllocator : public std::pmr::memory_resource {
    void* do_allocate(std::size_t bytes, std::size_t alignment) override {
        void* p = std::pmr::new_delete_resource()->allocate(bytes, alignment);
        std::cout << std::format("  do_allocate: {:6} bytes at {}\n", bytes, p);
        return p;
    }
 
    void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override {
        std::cout << std::format("  do_deallocate: {:4} bytes at {}\n", bytes, p);
        return std::pmr::new_delete_resource()->deallocate(p, bytes, alignment);
    }
 
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
        return std::pmr::new_delete_resource()->is_equal(other);
    }
};


int main() {

    std::cout << '\n';

    TrackAllocator trackAllocator;                         // (1)
    std::pmr::set_default_resource(&trackAllocator);       // (2)

    std::cout << "myVec1\n";

    std::array<std::byte, 200> buf1;
    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);
    }

    std::cout << "myVec2\n";

    char buf2[200] = {}; 
    std::pmr::monotonic_buffer_resource pool2{std::data(buf2), std::size(buf2)};
    std::pmr::vector<int> myVec2{&pool2};                  // (4)
    for (int i = 0; i < 200; ++i) {
        myVec2.push_back(i);
    }

    std::cout << '\n';

}
        

TrackAllocator is a tracking allocator. It derives from the interface class std::pmr::memory_resource, from which all memory resources derive. TrackAllocator defines the three required member functions do_allocate, do_deallocate, and do_is_equal. Each call forwards its call to std::pmr::new_delete_resource. std:pmr::new_delete_resource is the default memory resource and calls the global new and delete. The member function’s job do_is_equal is to check that the two memory resources are equal. The interesting point in the program is the visualization of the allocation and the deallocation in the member functions do_allocate and do_deallocate.

By instantiating the TrackAllocator (line 1) and make it as the default resource (line 2). myVec1 (line 3) and myVec2 (line 4) use them as the upstream allocator. This allocator kicks in if the primary allocator is consumed. This fallback is not necessary for myVec1, but necessary for myVec2.

This output shows the dynamical allocation and deallocation of myVec2 until all ints fit into the std::pmr::vector<int>.


Modernes C++ Mentoring

Be part of my mentoring programs:

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

 

You can also bind an upstream allocator to a specific container.

A Non-Allocating Allocator

std::pmr::null_resource_allocator is a special allocator. Using this memory resource for allocating causes a std::bad_alloc exception. This memory resource ensures that you do not arbitrarily allocate memory on the heap. Let’s try it out.

// nullMemoryResource.cpp

#include <array>
#include <cstddef>
#include <iostream> 
#include <memory_resource>
#include <string>
#include <vector>

int main() {

    std::cout << '\n';
 
    std::array<std::byte, 2000> buf;
    std::pmr::monotonic_buffer_resource pool{buf.data(), buf.size(),  // (1)
                                           std::pmr::null_memory_resource()};
    std::pmr::vector<std::pmr::string> myVec{&pool};                  // (2)
    try {
        for (int i = 0; i < 100; ++i) {                               // (3)
            std::cerr << i << " ";
            myVec.emplace_back("A short string");
        }
    }
    catch (const std::bad_alloc& e) {                                 // (4)
        std::cerr << '\n' << e.what() << '\n';
    }
    
    std::cout << '\n';
    
}
        

First, I allocate memory on the stack and initialize std::pmr::monotonic_buffer_resource with it. Then, I use this memory resource and std::pmr::null_memory_resource as an upstream allocator (line 1). In line 2, I create a std::pmr::vector<std::pmr::string>. Because I use a std::pmr::string, the string also uses the memory resource and its upstream allocator. When I use std::pmr::vector<std::string>, std::string allocates using the global new and delete. Finally, in line (3), I create 100 strings and catch in line (4) a std::bad_alloc exception. I got what I deserved. 100 strings will not fit into a std::array<std::byte, 2000> buffer. After 17 strings, the buffer is consumed.

What’s Next?

You may have heard it already. The std::pmr::monotonic_buffer has outstanding features. It is pretty fast and does not free memory. I will provide the number in my next post.


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, Bhavith C Achar, and Marco Parri Empoli.

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.

  • C++ – The Core Language
  • C++ – The Standard Library
  • C++ – Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++ Mentoring,



 



To view or add a comment, sign in

More articles by Rainer Grimm

  • Last Chance: 1 Day Left

    Last Chance: 1 Day Left

    December 19, 2024 Make the Difference Let’s do something great together: From December 1st to 24th, when you book one…

  • std::execution: More Senders

    std::execution: More Senders

    offers three types of senders: factories, adapters, and consumers. I’ll take a closer look at these today.

  • Christmas Special – 5 Days Left

    Christmas Special – 5 Days Left

    Make the Difference Let’s do something great together: From December 1st to 24th, when you book one of my mentoring…

  • Christmas Special – 7 Days Left

    Christmas Special – 7 Days Left

    Make the Difference Let’s do something great together: From December 1st to 24th, when you book one of my mentoring…

  • std::execution: Sender

    std::execution: Sender

    std::execution offers three types of senders: factories, adapters, and consumers. I’ll take a closer look at these…

  • My ALS Journey (18/n): C++ and ALS

    My ALS Journey (18/n): C++ and ALS

    Christmas Promotion Let’s do something great together: From December 1st to 24th, when you book one of my mentoring…

  • std::execution: Composition of Senders

    std::execution: Composition of Senders

    Most sender adaptors are composable using the pipe operator. Let me start with a simple example of composition with the…

  • std::execution: Inclusive Scan

    std::execution: Inclusive Scan

    Before I present the asynchronous inclusive scan, I introduce the inclusive scan, aka prefix sum. Prefix Sum In…

  • std::execution: Asynchronous Algorithms

    std::execution: Asynchronous Algorithms

    supports many asynchronous algorithms for various workflows. Presenting proposal P2300R10 is not easy.

  • My ALS Journey (17/n): Christmas Special

    My ALS Journey (17/n): Christmas Special

    Today, I have a special Christmas gift. My ALS Journey so far Make the Difference Let’s do something great together:…

Insights from the community

Others also viewed

Explore topics