BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslma_bslallocator

Macros

#define BSLMA_BSLALLOCATOR_DEPRECATE_ASSIGN
 

Detailed Description

Outline

Purpose

Provide an STL-compatible proxy for bslma::Allocator objects.

Classes

Canonical header: bsl_memory.h

See also
bslma_allocator

Description

This component provides an STL-compatible proxy for any allocator class derived from bslma::Allocator. The proxy class, bsl::allocator is a template that adheres to the allocator requirements defined in section 20.5.3.5 [allocator.requirements] of the C++17 standard. bsl::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. The bsl::allocator object holds a pointer to an object (the allocation mechanism) of class type derived from bslma::Allocator. Different mechanism types allocate memory in different ways or from different pools, so this approach gives the programmer run time control over how the container obtains memory.

The bsl::allocator template is intended to solve a problem created by the C++ standard allocator protocol. In STL, the allocator type is specified at compile time as a container template parameter, so the allocation mechanism becomes an explicit part of the resulting container type. Two containers cannot have the same type unless they are instantiated with the same allocator type. The bsl::allocator template breaks the connection between the compile-time allocator type and the run-time allocation mechanism. The allocation mechanism is chosen at run-time by initializing (contrast with instantiating) the bsl::allocator with a pointer to a mechanism object derived from bslma::Allocator. Each class derived from bslma::Allocator implements a specific allocation mechanism and is thus called a mechanism class within this component. The bsl::allocator object forwards calls made through the standard allocator interface to the mechanism object with which it was initialized. In this way, two containers instantiated with bsl::allocator can use different allocation mechanisms even though they have the same compile-time type. The default mechanism object, if none is supplied to the bsl::allocator constructor, is bslma::Default::defaultAllocator().

A container constructs its elements by calling the construct method on its allocator. Importantly, bsl::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 allocator type). Thus, a container instantiated with a scoped allocator ensures that its elements use the same allocator as the container itself. The bsl::allocator::construct method will propagate the allocator not only to element types that use bsl::allocator, but also to any types that use bslma::Allocator * – i.e., all type for which the bslma::UsesBslmaAllocator trait is true.

A container using bsl::allocator should not copy its allocator on assignment and thus assignment of bsl::allocator objects is almost always incorrect. Its base class, bsl::polymorphic_allocator, is not assignable, in fact but, for compatibility with some existing code, assignment of bsl::allocator must compile, but it is a precondition violation if the allocators being assigned are not already equal at run time (i.e., when assignment is a a no-op). The assignment operator is deprecated and might be removed in the future, once all existing uses have been excised.

Instantiations of bsl::allocator have reference semantics. A bsl::allocator object does not "own" the bslma::Allocator with which it is initialized; copying a bsl::allocator object does not copy its mechanism object and destroying a bsl::allocator does not destroy its mechanism object. Two bsl::allocator objects compare equal if and only if the mechanism objects they refer to compare equal.

Relationship to bsl::polymorphic_allocator

The bsl::allocator class template was the inspiration for the C++17 std::pmr::polymorphic_allocator class template (section 23.12.3, [mem.poly.allocator.class] in the C++17 Standard) and bslma::Allocator was the inspiration for the C++17 std::pmr::memory_resource (section 23.12.2, mem.res.class] in the C++17 Standard). For compatibility with the C++17 standard, bsl::allocator is derived from bsl::polymorphic_allocator which, when using a C++17 library, is identical to std::pmr::polymorphic_allocator. Similarly, bslma::Allocator is derived from bsl::memory_resource, which is identical to std::pmr::memory_resource. These inheritance relationships ensure that a bsl::allocator instance can be passed to any type that is instantiated with a std::pmr::polymorphic_allocator, including pmr containers from the platform library. Similarly, a pointer to bslma::Allocator is implicitly convertible to both std::pmr::memory_resource * and std::pmr::polymorphic_allocator.

C++03 Restrictions on Allocator Usage

The allocator requirements section of the C++03 standard (section 20.1.5 [lib.allocator.requirements]) permits containers to assume that two allocators of the same type always compare equal, effectively limiting C++03 to stateless allocators. This assumption is incorrect for instantiations of bsl::allocator. Therefore, for a container (or other facility) to use bsl::allocator, it must operate correctly in the presence of non-equal bsl::allocator objects. In practice, this means that a container cannot transfer ownership of allocated memory to another container unless the two containers use equal allocators. Older third-party templates that assume stateless allocators might not work correctly when instantiated with bsl::allocator.

Thread Safety

Because it is immutable, non-assignable, and has reference semantics, a single bsl::allocator object is safe for concurrent access by multiple threads if and only if the bslma::Allocator it references is safe for concurrent access from multiple threads. Separate objects of bsl::allocator type may safely be used in separate threads if and only if the bslma::Allocator objects they reference are, themselves, safe for concurrent access.

Usage

This section illustrates intended use of this component.

Example 1: A fixed size array

We first show how to define a container type parameterized with an STL-style allocator template parameter. To avoid issues concerning reallocation, dynamic growth, etc., we choose an array whose size is fixed at construction. Our array will accept any STL-compatible allocator; we do not assume as scoped allocator, which would dictate that we pass the allocator through to the parameterized T contained type (see the bslma_allocator component and bslma_constructionutil package).

We begin by defining member variables to hold the allocator, length, and allocated array:

/// This class provides an array of (the template parameter) `TYPE` of
/// fixed length as determined at construction time, using an instance
/// of (the template parameter) `ALLOC` type to supply memory.
template <class TYPE, class ALLOC>
class my_FixedSizeArray {
// DATA
ALLOC d_allocator;
int d_length;
TYPE *d_array;

Then, we define the public interface:

public:
// TYPES
typedef ALLOC allocator_type;
typedef TYPE value_type;
// CREATORS
/// Create a fixed-size array of the specified `length`, using the
/// optionally specified `allocator` to supply memory. If
/// `allocator` is not specified, a default-constructed instance of
/// the parameterized `ALLOC` type is used. All the elements in the
/// resulting array are default-constructed.
explicit my_FixedSizeArray(int length,
const ALLOC& allocator = ALLOC());
/// Create a copy of the specified `original` fixed-size array,
/// using the optionally specified `allocator` to supply memory. If
/// `allocator` is not specified, a default-constructed instance of
/// the parameterized `ALLOC` type is used.
my_FixedSizeArray(const my_FixedSizeArray& original,
const ALLOC& allocator = ALLOC());
/// Destroy this fixed size array.
~my_FixedSizeArray();
// MANIPULATORS
/// Assign to this array the value of the specified `original`
/// array. Note that the length of this array might change.
my_FixedSizeArray& operator=(const my_FixedSizeArray& original);
/// Return a reference to the modifiable element at the specified
/// `index` position in this fixed size array. The behavior is
/// undefined unless `index` is non-negative and less than
/// `length()`.
TYPE& operator[](int index) { return d_array[index]; }
// ACCESSORS
/// Return a reference to the non-modifiable element at the
/// specified `index` position in this fixed size array. The
/// behavior is undefined unless `index` is non-negative and less
/// than `length()`.
const TYPE& operator[](int index) const { return d_array[index]; }
/// Return the allocator used by this fixed size array to supply
/// memory.
allocator_type get_allocator() const { return d_allocator; }
/// Return the length specified at construction of this fixed size
/// array.
int length() const { return d_length; }
};
// FREE OPERATORS
/// Return `true` if the specified `lhs` fixed-size array has the same
/// value as the specified `rhs` fixed-size array, and `false`
/// otherwise. Two fixed-size arrays have the same value if they have
/// the same length and if the element at any index in `lhs` has the
/// same value as the corresponding element at the same index in `rhs`.
template<class TYPE, class ALLOC>
bool operator==(const my_FixedSizeArray<TYPE, ALLOC>& lhs,
const my_FixedSizeArray<TYPE, ALLOC>& rhs);
/// Return `true` if the specified `lhs` fixed-size array does not have
/// the same value as the specified `rhs` fixed-size array, and `false`
/// otherwise. Two fixed-size arrays have the same value if they have
/// the same length and if the element at any index in `lhs` has the
/// same value as the corresponding element at the same index in `rhs`.
template<class TYPE, class ALLOC>
bool operator!=(const my_FixedSizeArray<TYPE, ALLOC>& lhs,
const my_FixedSizeArray<TYPE, ALLOC>& rhs);
bool operator!=(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
bool operator==(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)

Next, we define the first constructor, which uses the allocator's allocate memory to obtain memory, then uses its construct method to construct each element. To provide a uniform and future-proof interface, the standard way to call allocate and construct is indrectly though bsl::allocator_traits. If ALLOC is a bsl::allocator object, then the construct method will attempt to pass the allocator to the constructed elements. Note that exception safety has been sacrificed for simplicity of presentation; a production version of my_FixedSizeArray would need to unwind any constructed elements and the allocation if an exception were thrown.

// CREATORS
template<class TYPE, class ALLOC>
my_FixedSizeArray<TYPE, ALLOC>::my_FixedSizeArray(int length,
const ALLOC& allocator)
: d_allocator(allocator), d_length(length)
{
d_array = Traits::allocate(d_allocator, d_length);
// Default construct each element of the array:
for (int i = 0; i < d_length; ++i) {
Traits::construct(d_allocator, &d_array[i]);
}
}
Definition bslma_allocatortraits.h:1061

Next, we define the copy constructor, which initializes the allocator member but defers the rest of the work to the assignment operator:

template<class TYPE, class ALLOC>
my_FixedSizeArray<TYPE, ALLOC>::my_FixedSizeArray(
const my_FixedSizeArray& original,
const ALLOC& allocator)
: d_allocator(allocator), d_length(0), d_array(0)
{
*this = original;
}

Now we define the assignment operator, which allocates the array and copies elements from the rhs array. Note, again, that we simplified the code by omitting exception-safety constructs.

template<class TYPE, class ALLOC>
my_FixedSizeArray<TYPE, ALLOC>&
my_FixedSizeArray<TYPE, ALLOC>::operator=(const my_FixedSizeArray& rhs)
{
if (this != &rhs) {
// Call destructor for each old element
for (int i = 0; i < d_length; ++i) {
Traits::destroy(d_allocator, &d_array[i]);
}
// Deallocate old storage
Traits::deallocate(d_allocator, d_array, d_length);
// Set length and allocate new array. Do not assign the allocator!
d_length = rhs.d_length;
d_array = Traits::allocate(d_allocator, d_length);
// Construct each element of the 'lhs' array from the corresponding
// 'rhs' element.
for (int i = 0; i < d_length; ++i) {
Traits::construct(d_allocator, &d_array[i], rhs.d_array[i]);
}
}
return *this; // RETURN
}

Next, we define the destructor, which uses the allocator's destroy method to destroy each element, then the allocator's deallocate method to return memory to the allocator:

template<class TYPE, class ALLOC>
my_FixedSizeArray<TYPE, ALLOC>::~my_FixedSizeArray()
{
// Call destructor for each element
for (int i = 0; i < d_length; ++i) {
Traits::destroy(d_allocator, &d_array[i]);
}
// Return memory to allocator.
Traits::deallocate(d_allocator, d_array, d_length);
}

The equality and inequality operators simply compare the lengths and element values of the two arrays:

// FREE OPERATORS
template<class TYPE, class ALLOC>
bool operator==(const my_FixedSizeArray<TYPE, ALLOC>& lhs,
const my_FixedSizeArray<TYPE, ALLOC>& rhs)
{
if (lhs.length() != rhs.length()) {
return false; // RETURN
}
for (int i = 0; i < lhs.length(); ++i) {
if (lhs[i] != rhs[i]) {
return false; // RETURN
}
}
return true;
}
template<class TYPE, class ALLOC>
inline
bool operator!=(const my_FixedSizeArray<TYPE, ALLOC>& lhs,
const my_FixedSizeArray<TYPE, ALLOC>& rhs) {
return ! (lhs == rhs);
}

Now we can create array objects with different allocator mechanisms. First we create an array, a1, using the default allocator and fill it with the values 1 .. 5:

int main() {
my_FixedSizeArray<int, bsl::allocator<int> > a1(5);
assert(5 == a1.length());
assert(bslma::Default::defaultAllocator() == a1.get_allocator());
for (int i = 0; i < a1.length(); ++i) {
a1[i] = i + 1;
}
static Allocator * defaultAllocator()
Definition bslma_default.h:889

Finally, we create a copy of a1 using a test allocator. The values of a1 and a2 are equal, even though they have different allocation mechanisms. We verify that the test allocator was used to allocate the new array elements:

my_FixedSizeArray<int, bsl::allocator<int> > a2(a1, &testAlloc);
assert(a1 == a2);
assert(a1.get_allocator() != a2.get_allocator());
assert(&testAlloc == a2.get_allocator());
assert(1 == testAlloc.numBlocksInUse());
}
Definition bslma_testallocator.h:384
bsls::Types::Int64 numBlocksInUse() const
Definition bslma_testallocator.h:1087

Example 2: Propagation of the Allocator to Elements

In this example, we use the FixedSizeArray template defined in Example 1 and demonstrate how bsl::allocator propagates itself to the elements it constructs, such that the container and its elements all use the same allocator.

First, we create a representative element class, MyType, that allocates memory using the bslma::Allocator protocol:

#include <bslma_default.h>
class MyType {
bslma::Allocator *d_allocator_p;
// etc.
public:
// TRAITS
// CREATORS
explicit MyType(bslma::Allocator* basicAlloc = 0)
: d_allocator_p(bslma::Default::allocator(basicAlloc)) { /* ... */ }
MyType(const MyType&, bslma::Allocator* basicAlloc = 0)
: d_allocator_p(bslma::Default::allocator(basicAlloc)) { /* ... */ }
// etc.
// ACCESSORS
bslma::Allocator *allocator() const { return d_allocator_p; }
// etc.
};
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bslma_allocator.h:457
Definition balxml_encoderoptions.h:68
Definition bslma_usesbslmaallocator.h:343

Now, we instantiate my_FixedSizeArray using MyType and verify that, when we provide the address of an allocator to the constructor of the container, the same address is passed to the constructor of the container's elements:

#include <bslmf_issame.h>
int main()
{
typedef my_FixedSizeArray<MyType, bsl::allocator<MyType> > ArrayType;
const int arrayLen = 7;
ArrayType C1(arrayLen, &testAlloc);
assert((bsl::is_same<ArrayType::allocator_type,
assert(C1.get_allocator() == bsl::allocator<MyType>(&testAlloc));
for (int i = 0; i < arrayLen; ++i) {
assert(C1[i].allocator() == &testAlloc);
}
Definition bslma_bslallocator.h:580
Definition bslmf_issame.h:146

Next, we copy-construct the container and verify that the copy uses the default allocator, not the allocator from the original; moreover, we verify that the elements stored in the copy also use the default allocator.

ArrayType C2(C1);
assert(C2.get_allocator() != C1.get_allocator());
assert(C2.get_allocator() == bsl::allocator<MyType>());
for (int i = 0; i < arrayLen; ++i) {
assert(C2[i].allocator() != &testAlloc);
assert(C2[i].allocator() == bslma::Default::defaultAllocator());
}

Finally, we create a third array using the test allocator and use assignment to give it the same value as the second array. We then verify that the assignment did not modify the allocator of the lhs array and that the elements of the resulting copy use the same allocator as the lhs array:

ArrayType C3(1, &testAlloc2);
assert(1 == testAlloc2.numBlocksInUse());
assert(1 == C3.length());
assert(C3.get_allocator() == bsl::allocator<MyType>(&testAlloc2));
assert(C3[0].allocator() == &testAlloc2);
C3 = C2; // Assignment
assert(1 == testAlloc2.numBlocksInUse());
assert(arrayLen == C3.length());
assert(C3.get_allocator() == bsl::allocator<MyType>(&testAlloc2));
for (int i = 0; i < arrayLen; ++i) {
assert(C3[i].allocator() == &testAlloc2);
}
}

Macro Definition Documentation

◆ BSLMA_BSLALLOCATOR_DEPRECATE_ASSIGN

#define BSLMA_BSLALLOCATOR_DEPRECATE_ASSIGN
Value:
BSLS_DEPRECATE_FEATURE("bsl", "bsl_allocator_assign", \
"Do not assign allocators.")
#define BSLS_DEPRECATE_FEATURE(UOR, FEATURE, MESSAGE)
Definition bsls_deprecatefeature.h:319