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:
class Data1 {
public:
: d_allocator_p(basicAllocator) { }
};
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 {
Data1 d_data;
public:
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):
class Data2 {
public:
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 {
Data2 d_data;
public:
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 {
int d_value;
public:
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(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:
}
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() {
}
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;
if (Propagate::value || d_allocator == other.d_allocator) {
Propagate());
swap(d_value_p, other.d_value_p);
}
else
{
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):
template <class t_TYPE>
class MyAlloc {
public:
typedef t_TYPE value_type;
MyAlloc() { }
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());
}