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

Detailed Description

Outline

Purpose

Provide a namespace for utility functions on allocators.

Classes

See also
bslma_aatypeutil, bslma_allocatortraits

Description

This component provides a namespace struct, bslma::AllocatorUtil, with functions that operate on both raw pointers to bslma::Allocator or derived classes and objects of C++11 compliant allocator classes. The functions in this utility struct also free the user from worrying about rebinding the allocator and creating copies of rebound allocators. Operations provided include allocateBytes and deallocateBytes to acquire and free raw bytes; allocateObject and deallocateObject to aquire and free uninitialized object storages; and newObject and deleteObject to allocate+construct and destroy+deallocate full objects. There are also operations for conditionally assigning or swapping allocator objects themselves, depending on the allocator's propagation traits.

Usage

Example 1: Future-proofing Member Construction

This example shows how we construct an AA member variable, using bslma::AllocatorUtil::adapt so that it is both self-documenting and robust in case the member type is modernized from legacy-AA (using bslma::Allocator * directly in its interface) to bsl-AA (using bsl::allocator in its interface).

First, we define a class, Data1, that has a legacy-AA interface:

/// Legacy-AA data class.
class Data1 {
bslma::Allocator *d_allocator_p;
// ...
public:
explicit Data1(bslma::Allocator *basicAllocator = 0)
: d_allocator_p(basicAllocator) { /* ... */ }
bslma::Allocator *allocator() const { return d_allocator_p; }
};
Definition bslma_allocator.h:457

Next, we define a class, MyClass1, that has a member of type Data1. MyClass uses a modern, bsl-AA interface:

class MyClass1 {
bsl::allocator<char> d_allocator;
Data1 d_data;
public:
typedef bsl::allocator<char> allocator_type;
explicit MyClass1(const allocator_type& allocator = allocator_type());
const Data1& data() const { return d_data; }
allocator_type get_allocator() const { return d_allocator; }
};
Definition bslma_bslallocator.h:580

Next, we define the constructor for MyClass1. Since MyClass1 uses bsl::allocator and the Data1 uses bslma::Allocator *, we employ bslma::AllocatorUtil::adapt to obtain an allocator suitable for passing to the constructor for d_data:

MyClass1::MyClass1(const allocator_type& allocator)
: d_allocator(allocator)
, d_data(bslma::AllocatorUtil::adapt(allocator))
{
}
Definition balxml_encoderoptions.h:68

Next, assume that we update our Data class from legacy-AA to bsl-AA (renamed from Data1 to Data2 for illustrative purposes):

/// Bsl-AA data class.
class Data2 {
bsl::allocator<int> d_allocator;
// ...
public:
typedef bsl::allocator<int> allocator_type;
explicit Data2(const allocator_type& allocator = allocator_type())
: d_allocator(allocator) { /* ... */ }
allocator_type get_allocator() const { return d_allocator; }
};

Now, we notice that nothing about MyClass needs to change, not even the way its constructor passes an allocator to d_data:

class MyClass2 {
bsl::allocator<char> d_allocator;
Data2 d_data;
public:
typedef bsl::allocator<char> allocator_type;
explicit MyClass2(const allocator_type& allocator = allocator_type());
const Data2& data() const { return d_data; }
allocator_type get_allocator() const { return d_allocator; }
};
MyClass2::MyClass2(const allocator_type& allocator)
: d_allocator(allocator)
, d_data(bslma::AllocatorUtil::adapt(allocator))
{
}

Finally, we test both versions of MyClass and show that the allocator that is passed to the MyClass constructor gets forwarded to its data member:

int main()
{
MyClass1 obj1(alloc);
assert(&ta == obj1.data().allocator());
MyClass2 obj2(alloc);
assert(alloc == obj2.data().get_allocator());
}
Definition bslma_testallocator.h:384

Example 2: Building an AA object on the heap

This example shows how we can allocate a bsl-AA object from an allocator and construct the object, passing the allocator along, in one step.

First, we define a simple class, BslAAType, that uses bsl::allocator to allocate memory (i.e., it is bsl-AA):

class BslAAType {
bsl::allocator<> d_allocator;
int d_value;
public:
typedef bsl::allocator<> allocator_type;
explicit BslAAType(const allocator_type& a = allocator_type())
: d_allocator(a), d_value(0) { }
explicit BslAAType(int v, const allocator_type& a = allocator_type())
: d_allocator(a), d_value(v) { }
allocator_type get_allocator() const { return d_allocator; }
int value() const { return d_value; }
};

Now we can use bslma::AllocatorUtil::newObject to, in a single operation, allocate and construct an BslAAType object. We can see that the right allocator and value are passed to the new object:

int main()
{
BslAAType *p = bslma::AllocatorUtil::newObject<BslAAType>(&ta, 77);
assert(sizeof(BslAAType) == ta.numBytesInUse());
assert(77 == p->value());
assert(&ta == p->get_allocator().mechanism());
bsls::Types::Int64 numBytesInUse() const
Definition bslma_testallocator.h:1111

Finally, we use deleteObject to destroy and return the object to the allocator:

assert(0 == ta.numBytesInUse());
}
static void deleteObject(const t_ALLOCATOR &allocator, t_POINTER p)
Definition bslma_allocatorutil.h:936

Example 3: Safe container swap

In this example, we see how bslma::AllocatorUtil::swap can be used to swap allocators without the risk of calling a non-existant swap.

First, we create a class, StdAAType, that uses any valid STL-compatible allocator (i.e., it is stl-AA). Note that this class has non-default copy constructor and assignment operations (whose implementation is not shown) and a non-default swap operation:

template <class t_TYPE, class t_ALLOCATOR = bsl::allocator<t_TYPE> >
class StlAAType {
t_ALLOCATOR d_allocator;
t_TYPE *d_value_p;
public:
typedef t_ALLOCATOR allocator_type;
explicit StlAAType(const allocator_type& a = allocator_type())
: d_allocator(a)
, d_value_p(bslma::AllocatorUtil::newObject<t_TYPE>(a)) { }
explicit StlAAType(const t_TYPE& v,
const allocator_type& a = allocator_type())
: d_allocator(a)
, d_value_p(bslma::AllocatorUtil::newObject<t_TYPE>(a, v)) { }
StlAAType(const StlAAType&);
~StlAAType() {
bslma::AllocatorUtil::deleteObject(d_allocator, d_value_p);
}
StlAAType operator=(const StlAAType&);
void swap(StlAAType& other);
allocator_type get_allocator() const { return d_allocator; }
const t_TYPE& value() const { return *d_value_p; }
};
template <class t_TYPE, class t_ALLOCATOR>
inline void swap(StlAAType<t_TYPE, t_ALLOCATOR>& a,
StlAAType<t_TYPE, t_ALLOCATOR>& b)
{
a.swap(b);
}
void swap(OptionValue &a, OptionValue &b)

Next, we write the swap member function. This function should follow our standard AA rule for member swap: if the allocators compare equal or if the allocators should propagate on swap, then perform a fast swap, moving only pointers and (possibly) allocators, rather than copying elements; otherwise revert to element-by-element swap:

template <class t_TYPE, class t_ALLOCATOR>
void StlAAType<t_TYPE, t_ALLOCATOR>::swap(StlAAType& other)
{
typedef typename
Propagate;
using std::swap;
if (Propagate::value || d_allocator == other.d_allocator) {
// Swap allocators and pointers, but not individual elements.
bslma::AllocatorUtil::swap(&d_allocator, &other.d_allocator,
Propagate());
swap(d_value_p, other.d_value_p);
}
else
{
// Swap element values
swap(*d_value_p, *other.d_value_p);
}
}
void swap(TYPE &a, TYPE &b)
BloombergLP::bslma::AllocatorTraits_PropOnSwap< ALLOCATOR_TYPE >::type propagate_on_container_swap
Definition bslma_allocatortraits.h:1315
static void swap(t_TYPE *pa, t_TYPE *pb, bsl::false_type allowed)
Definition bslma_allocatorutil.h:1024

Note that, in the above implementation of swap, that we swap the allocators using bslma::AllocatorUtil::swap instead of calling swap directly. If the t_ALLOCATOR type does not propagate on container assignment or swap, the allocator itself is not required to support assignment or swap. By using this utility, we avoid trying to compile a call to allocator swap when it is not needed.

Next, we'll define an allocator that illustrates this point. Our MyAlloc allocator does not support allocator propogation and deletes the assignment operators (thus also disabling swap):

#include <bsls_keyword.h>
template <class t_TYPE>
class MyAlloc {
// Disable assignment
MyAlloc operator=(const MyAlloc&) BSLS_KEYWORD_DELETED;
public:
typedef t_TYPE value_type;
MyAlloc() { }
MyAlloc(bslma::Allocator *allocPtr) : d_imp(allocPtr) { } // IMPLICIT
template <class U>
MyAlloc(const MyAlloc<U>& other) : d_imp(other.d_imp) { }
t_TYPE *allocate(std::size_t n) { return d_imp.allocate(n); }
void deallocate(t_TYPE* p, std::size_t n) { d_imp.deallocate(p, n); }
template <class T2>
friend bool operator==(const MyAlloc& a, const MyAlloc<T2>& b)
{ return a.d_imp == b.d_imp; }
template <class T2>
friend bool operator!=(const MyAlloc& a, const MyAlloc<T2>& b)
{ return a.d_imp != b.d_imp; }
};
void deallocate(TYPE *p, std::size_t n=1)
Definition bslma_bslallocator.h:1039
BSLS_ANNOTATION_NODISCARD pointer allocate(size_type n, const void *hint=0)
Definition bslma_bslallocator.h:1032
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:609
bool operator!=(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
bool operator==(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)

Finally, we create two StlAAType objects with the same allocator and show that they can be swapped even though the allocator type cannot be swapped:

int main()
{
MyAlloc<int> alloc;
StlAAType<int, MyAlloc<int> > objA(1, alloc), objB(2, alloc);
assert(alloc == objA.get_allocator());
assert(alloc == objB.get_allocator());
assert(1 == objA.value());
assert(2 == objB.value());
objA.swap(objB);
assert(2 == objA.value());
assert(1 == objB.value());
}