Low-Level Memory

Polymorphic Memory Resources

A regular allocator is a part of the type of a container. If you change the allocator type, then every vector, list, or map that uses it is now a different type. Polymorphic memory resources take the guesswork out of which memory location your container will use by deferring that choice until runtime.

A memory resource is just an object that knows how to allocate and deallocate raw bytes; they all descend from an abstract base class std::pmr::memory_resource.

Containers that use a std::pmr::polymorphic_allocator contain a pointer to a memory_resource and delegate every allocation or deallocation call to the pointed to resource. Because the container’s polymorphic_allocator type is fixed, the container’s overall type doesn’t change as the resource changes.

The simplest resource is the global heap:

#include <iostream>
#include <vector>
#include <memory_resource>

int main()
{
    std::pmr::vector<int> v{std::pmr::new_delete_resource()};
    // Append a new element to the container
    v.push_back(10);
    v.push_back(20);
    std::cout << v[0] << ' ' << v[1] << '\n';
}

std::pmr::new_delete_resource() returns a pointer to a memory_resource which performs allocations and deallocations via ::operator new and ::operator delete. We provide that pointer to the std::pmr::vector constructor, and the vector stores the pointer internally inside its polymorphic allocator.

Whenever the vector requires memory, it calls that resource’s allocate function and upon destruction it calls deallocate().

Both retrieved values are then stored and displayed just like with any other std::vector.

The second type of standard resource that you will see often is the one that acquires memory from a static buffer and won’t release it again until the resource is destroyed:

#include <iostream>
#include <vector>
#include <memory_resource>

int main()
{
    unsigned char buffer[1024]{};
    std::pmr::monotonic_buffer_resource resource{buffer, sizeof(buffer)};
    // Pass this resource object's address to the constructor
    std::pmr::vector<int> v{&resource};
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    std::cout << v[0] << ' ' << v[1] << ' ' << v[2] << '\n';
}

An array, buffer, is declared and is given a capacity of 1024 raw bytes. We then create the monotonic_buffer_resource with the address of our buffer and its size and then provide that resource object to the std::pmr::vector.

Now any call to memory required by the vector is serviced directly from our buffer; when the vector is destroyed, none of the returned memory will be deallocated, as the entire resource is destroyed and can never again request a deallocation. We insert, retrieve, and display the three values in the same way we did before.

Notice that both std::pmr::vector types are the same, and only the underlying resource that was requested at creation differs.

When your code is supplied with a std::pmr::vector, you may never know what underlying memory_resource that container is using.

Our simple implementation chooses the std::pmr::new_delete_resource on our part, and we allow the user to ask the std::pmr::monotonic_buffer_resource to perform storage by supplying that resource to our user. You must ensure that you allocate an amply large buffer yourself or consider providing a secondary, fallback std::pmr::memory_resource object that the std::pmr::monotonic_buffer_resource can use to fall back to in the case its buffer is full. Providing a null pointer as a resource also causes undefined behavior: A std::pmr::memory_resource object must always be used.

The most important advantage here is that we managed to separate the container type, a plain std::pmr::vector, from the underlying policy of how it requests its memory.

The concrete resource may change, be replaced, or be injected entirely at runtime, but the type of the container remains constant, simplifying your code drastically.

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.