| Home | Libraries | People | FAQ | More

## Extended Functionality: Polymorphic Memory Resources

The document C++ Extensions for Library Fundamentals (Final draft: N4480) includes classes that provide allocator type erasure and runtime polymorphism. As Pablo Halpern, the author of the proposal, explains in the paper (N3916 Polymorphic Memory Resources (r2)):

“A significant impediment to effective memory management in C++ has been the inability to use allocators in non-generic contexts. In large software systems, most of the application program consists of non-generic procedural or object-oriented code that is compiled once and linked many times.”

“Allocators in C++, however, have historically relied solely on compile-time polymorphism, and therefore have not been suitable for use in vocabulary types, which are passed through interfaces between separately-compiled modules, because the allocator type necessarily affects the type of the object that uses it. This proposal builds upon the improvements made to allocators in C++11 and describes a set of facilities for runtime polymorphic memory resources that interoperate with the existing compile-time polymorphic allocators.”

Boost.Container implements nearly all classes of the proposal under the namespace `boost::container::pmr`. There are two groups,

- Header only utilities (these don't require the separately compiled library):
  - `memory_resource`.
  - `resource_adaptor`.
- Utilities that require the the separately compiled library:
  - `polymorphic_allocator`.
  - `monotonic_buffer_resource`.
  - `unsynchronized_pool_resource`.
  - `synchronized_pool_resource`.
  - Global resource functions: `get_default_resource`/ `set_default_resource`/ `new_delete_resource`/ `null_memory_resource`
  - Aliases for boost containers using the polymorphic allocator (like `pmr::vector`, etc.)

Boost.Container's polymorphic resource library is usable from C++03 containers, and offers some alternative utilities if the required C++11 features of the Library Fundamentals specification are not available.

Let's review the usage example given in N3916 and see how it can be implemented using Boost.Container: Suppose we are processing a series of shopping lists, where a shopping list is a container of strings, and storing them in a collection (a list) of shopping lists. Each shopping list being processed uses a bounded amount of memory that is needed for a short period of time, while the collection of shopping lists uses an unbounded amount of memory and will exist for a longer period of time. For efficiency, we can use a more time-efficient memory allocator based on a finite buffer for the temporary shopping lists.

Let's see how `ShoppingList` can be defined to support an polymorphic memory resource that can allocate memory from different underlying mechanisms. The most important details are:

- It should declare that supports an allocator defining an `allocator_type` typedef. This `allocator_type` will be of type `memory_resource *`, which is a base class for polymorphic resources.
- It must define constructors that take the the allocator as argument. It can be implemented in two ways:
  - `ShoppingList` has constructors taking `memory_resource*` as the last argument.
  - `ShoppingList` has constructors taking `allocator_arg_t` as the first argument and `memory_resource*` as the second argument.

Note: In C++03 compilers, it is required that the programmer specializes as `true` `constructible_with_allocator_suffix` or `constructible_with_allocator_prefix` as in C++03 there is no way to automatically detect the chosen option at compile time. If no specialization is done, Boost.Container assumes the suffix option.

    //ShoppingList.hpp
    #include <boost/container/pmr/vector.hpp>
    #include <boost/container/pmr/string.hpp>

    class ShoppingList
    {
       // A vector of strings using polymorphic allocators. Every element
       // of the vector will use the same allocator as the vector itself.
       boost::container::pmr::vector_of
          <boost::container::pmr::string>::type m_strvec;
       //Alternatively in compilers that support template aliases:
       //    boost::container::pmr::vector<boost::container::pmr::string> m_strvec;
       public:

       // This makes uses_allocator<ShoppingList, memory_resource*>::value true
       typedef boost::container::pmr::memory_resource* allocator_type;

       // If the allocator is not specified, "m_strvec" uses pmr::get_default_resource().
       explicit ShoppingList(allocator_type alloc = 0)
          : m_strvec(alloc) {}

       // Copy constructor. As allocator is not specified,
       // "m_strvec" uses pmr::get_default_resource().
       ShoppingList(const ShoppingList& other)
          : m_strvec(other.m_strvec) {}

       // Copy construct using the given memory_resource.
       ShoppingList(const ShoppingList& other, allocator_type a)
          : m_strvec(other.m_strvec, a) {}

       allocator_type get_allocator() const
       { return m_strvec.get_allocator().resource(); }

       void add_item(const char *item)
       { m_strvec.emplace_back(item); }

       //...
    };

However, this time-efficient allocator is not appropriate for the longer lived collection of shopping lists. This example shows how those temporary shopping lists, using a time-efficient allocator, can be used to populate the long lived collection of shopping lists, using a general purpose allocator, something that would be annoyingly difficult without the polymorphic allocators.

In Boost.Container for the time-efficient allocation we can use `monotonic_buffer_resource`, providing an external buffer that will be used until it's exhausted. In the default configuration, when the buffer is exhausted, the default memory resource will be used instead.

    #include "ShoppingList.hpp"
    #include <cassert>
    #include <boost/container/pmr/list.hpp>
    #include <boost/container/pmr/monotonic_buffer_resource.hpp>

    void processShoppingList(const ShoppingList&)
    {  /**/   }

    int main()
    {
       using namespace boost::container;
       //All memory needed by folder and its contained objects will
       //be allocated from the default memory resource (usually new/delete)
       pmr::list_of<ShoppingList>::type folder; // Default allocator resource
       //Alternatively in compilers that support template aliases:
       //    boost::container::pmr::list<ShoppingList> folder;
       {
          char buffer[1024];
          pmr::monotonic_buffer_resource buf_rsrc(&buffer, 1024);

          //All memory needed by temporaryShoppingList will be allocated
          //from the local buffer (speeds up "processShoppingList")
          ShoppingList temporaryShoppingList(&buf_rsrc);
          assert(&buf_rsrc == temporaryShoppingList.get_allocator());

          //list nodes, and strings "salt" and "pepper" will be allocated
          //in the stack thanks to "monotonic_buffer_resource".
          temporaryShoppingList.add_item("salt");
          temporaryShoppingList.add_item("pepper");
          //...

          //All modifications and additions to "temporaryShoppingList"
          //will use memory from "buffer" until it's exhausted.
          processShoppingList(temporaryShoppingList);

          //Processing done, now insert it in "folder",
          //which uses the default memory resource
          folder.push_back(temporaryShoppingList);
          assert(pmr::get_default_resource() == folder.back().get_allocator());
          //temporaryShoppingList, buf_rsrc, and buffer go out of scope
       }
       return 0;
    }

Notice that the shopping lists within `folder` use the default allocator resource whereas the shopping list `temporaryShoppingList` uses the short-lived but very fast `buf_rsrc`. Despite using different allocators, you can insert `temporaryShoppingList` into folder because they have the same `ShoppingList` type. Also, while `ShoppingList` uses memory_resource directly, `pmr::list`, `pmr::vector` and `pmr::string` all use `polymorphic_allocator`.

The resource passed to the `ShoppingList` constructor is propagated to the vector and each string within that `ShoppingList`. Similarly, the resource used to construct `folder` is propagated to the constructors of the ShoppingLists that are inserted into the list (and to the strings within those `ShoppingLists`). The `polymorphic_allocator` template is designed to be almost interchangeable with a pointer to `memory_resource`, thus producing a bridge between the template-policy style of allocator and the polymorphic-base-class style of allocator.

This example actually shows how easy is to use Boost.Container to write type-erasured allocator-capable classes even in C++03 compilers.