|
BDE 4.14.0 Production release
|
Provide an allocator interface for bsl::memory_resource objects.
Canonical header: bsl_memory_resource.h
This component provides an STL-compatible proxy for any resource class derived from bsl::memory_resource. The bsl::polymorphic_allocator interface is identical to that of std::pmr::polymorphic_allocator from the C++17 Standard Library; in fact, the former type is an alias for the latter type when using a C++17 or later library supplied by the platform.
The proxy class, bsl::polymorphic_allocator is a template that adheres to the allocator requirements defined in section [allocator.requirements] of the C++ standard. bsl::polymorphic_allocator may be used to instantiate any class template that is parameterized by a standard allocator. The container is expected to allocate memory for its own use through the allocator. A bsl::polymorphic_allocator object is initialized using a pointer to a resource object derived from bsl::memory_resource. Different types of memory resources use different allocation mechanisms, so this approach gives the programmer run time control over how the container obtains memory.
A container constructs its elements by calling the construct method on its allocator. Importantly, bsl::polymorphic_allocator is a scoped allocator – when its construct method is called, the allocator passes itself to the constructor of the object being constructed (if that object is allocator aware (AA) and uses a compatible the allocator type). Thus, a container instantiated with a scoped allocator ensures that its elements use the same allocator as the container itself.
A container using bsl::polymorphic_allocator should not copy its allocator on assignment and thus, to avoid errors, bsl::polymorphic_allocator, is not assignable. By design, a member of type bsl::polymorphic_allocator will prevent the client class from having a compiler-generated (defaulted) assignment operator because such an assignment operator would almost certainly do the wrong thing – copying the allocator and allocated objects instead of cloning those objects using the destination allocator. Once constructed, there is no straightforward way to rebind a bsl::polymorphic_allocator to use a different resource.
Instantiations of bsl::polymorphic_allocator have reference semantics. A bsl::polymorphic_allocator object does not "own" the bslma::Allocator with which it is initialized; copying a bsl::polymorphic_allocator object does not copy its resource object and destroying a bsl::polymorphic_allocator does not destroy its resource object. Two bsl::polymorphic_allocator objects compare equal if and only if the resource objects they refer to compare equal.
Because it is immutable, non-assignable, and has reference semantics, a single bsl::polymorphic_allocator object is safe for concurrent access by multiple threads if and only if the bsl::memory_resource it references is safe for concurrent access from multiple threads. Separate objects of bsl::polymorphic_allocator type may safely be used in separate threads if and only if the bsl::memory_resource objects they reference are, themselves, safe for concurrent access.
This section illustrates intended use of this component.
In this example, we define a class template, Holder<TYPE>, that holds a single instance of TYPE on the heap. Holder is designed such that its memory use can be customized by supplying an appropriate allocator. A holder object can be empty and it can be move-constructed even if TYPE is not movable. In addition, the footprint of a Holder object is the same (typically the size of 2 pointers), regardless of the size of TYPE.
First, we create a CountingResource class, derived from bsl::memory_resource, that keeps track of the number of blocks of memory that were allocated from the resource but not yet returned to the resource; see usage example 1 in bslma_memoryresource .
Now we define our actual Holder template with with data members to hold the memory allocator and a pointer to the contained object:
Next, we declare the constructors. Following the pattern for allocator-aware types used in BDE, the public interface contains an allocator_type typedef that can be passed to each constructor.:
Next, we declare the manipulators and accessors, allowing a Holder to be assigned and giving a client access to its value and allocator:
Next, we'll implement the first constructor, which creates an empty object; its only job is to store the allocator:
Next, we'll implement the second constructor, which allocates memory and constructs an object in it. The try/catch block is needed to free the memory in case the constructor for TYPE throws and exception. An alternative implementation would use an RAII object to automatically free the memory in the case of an exception (see bslma_deallocatorproctor ):
Next, we'll implement a destructor that deletes the value object and deallocates the allocated memory:
Finally, we've implemented enough of Holder to demonstrate its use. Below, we pass the CountingResource from Example 1 to the constructors several Holder objects. Each non-empty Holder allocates one block of memory, which is reflected in the outstanding block count. Note that the address of the resource can be passed directly to the constructors because bsl::polymorphic_allocator is implicitly convertible from bsl::memory_resource *: