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

Detailed Description

Outline

Purpose

Provide a namespace for utility functions on allocator-aware types.

Classes

See also
bslma_aamodel

Description

This component provides a namespace struct, bslma::AATypeUtil, that provides functions for extracting the allocator from an allocator-aware (AA) type. The functions in this struct choose the appropriate machinery to get the allocator from an object of a specified TYPE based on the AA model supported by TYPE. For example, if AAModel<TYPE> is AAModelLegacy (see bslma_aamodel ), then the allocator is extracted using the allocator mechanism. Some of the methods in this component are used to request allocators of a specific type; if the object's allocator is not convertible to that type, then the program will not compile.

Usage

Example 1: Constructing a New Member Using an Existing Member's Allocator

This example illustrates how bslma::AATypeUtil::getAdaptedAllocator can be used to extract the allocator from an Allocator-Aware (AA) object and use it to construct a different AA object without regard to whether either object is legacy-AA (using bslma::Allocator *) or bsl-AA (using bsl::allocator). We begin by defining two legacy-AA classes, Larry, and Curly:

/// A **legacy-AA** class.
class Larry {
// DATA
bslma::Allocator *d_allocator_p;
int d_value;
public:
// TYPE TRAITS
// CREATORS
explicit Larry(int v, bslma::Allocator *basicAllocator = 0)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
, d_value(v) { }
// ACCESSORS
bslma::Allocator *allocator() const { return d_allocator_p; }
int value() const { return d_value; }
};
class Curly {
// Another **legacy-AA** class.
// DATA
bslma::Allocator *d_allocator_p;
int d_value;
public:
// TYPE TRAITS
// CREATORS
explicit Curly(int v, bslma::Allocator *basicAllocator = 0)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
, d_value(v) { }
// ACCESSORS
bslma::Allocator *allocator() const { return d_allocator_p; }
int value() const { return d_value; }
};
#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

Next, consider a class, LarryMaybeCurly, that holds a Larry object and optionally, holds a Curly object. The data members for LarryMaybeCurly include a Larry object, a flag indicating the existence of a Curly object, and an aligned buffer to hold the optional Curly object, if it exists. Because the Larry member holds an allocator, there is no need for a separate allocator data member:

class LarryMaybeCurly {
// Holds a 'Larry' object and possibly a 'Curly' object.
// DATA
bool d_hasCurly; // True if 'd_curly' is populated
Larry d_larry;
bsls::ObjectBuffer<Curly> d_curly; // Maybe holds a 'Curly' object
Definition bsls_objectbuffer.h:276

Next we complete the public interface, which includes a constructor that sets the value of the Larry object, a manipulator for setting the value of the Curly object, and accessors for retrieving the Larry and Curly objects. Because LarryMaybeCurly is allocator-aware (AA), we must have an allocator_type member, and a get_allocator accessor; every constructor should also take an optional allocator argument.

public:
// TYPES
typedef bsl::allocator<char> allocator_type;
// CREATORS
explicit LarryMaybeCurly(int v,
const allocator_type& a = allocator_type());
// Create an object having a 'Larry' member with the specified 'v'
// value and having no 'Curly' member. Optionally specify an
// allocator 'a' to supply memory.
// ...
// MANIPULATORS
void setCurly(int v);
// initialize the 'Curly' member to the specified 'v' value.
// ACCESSORS
bool hasCurly() const { return d_hasCurly; }
const Larry& larry() const { return d_larry; }
const Curly& curly() const { return d_curly.object(); }
allocator_type get_allocator() const;
};
Definition bslma_bslallocator.h:580
TYPE & object()
Definition bsls_objectbuffer.h:351

Now we implement the constructor that initializes value of the Larry member and leaves the Curly member unset. Notice that we use bslma::AllocatorUtil::adapt to smooth out the mismatch between the bsl::allocator used by LarryMaybeCurly and the bslma::Allocator * expected by Larry.

LarryMaybeCurly::LarryMaybeCurly(int v, const allocator_type& a)
: d_hasCurly(false), d_larry(v, bslma::AllocatorUtil::adapt(a)) { }

Next, we implement the manipulator for setting the Curly object. This manipulator must use the allocator stored in d_larry. The function, getAdaptedAllocator yields this allocator in a form that can be consumed by the Curly constructor:

// MANIPULATORS
void LarryMaybeCurly::setCurly(int v)
{
new (d_curly.address())
d_hasCurly = true;
}
static bsl::enable_if< AAModel< TYPE >::value==AAModelLegacy::value||AAModel< TYPE >::value==AAModelBsl::value, bslma::Allocator * >::type getAdaptedAllocator(const TYPE &object)
Definition bslma_aatypeutil.h:459
TYPE * address()
Definition bsls_objectbuffer.h:334

Finally, we can use a test allocator to verify that, when a LarryMaybeCurly object is constructed with an allocator, that same allocator is used to construct both the Larry and Curly objects within it:

int main()
{
bsl::allocator<char> bslAlloc(&ta);
LarryMaybeCurly obj1(5, bslAlloc);
assert(5 == obj1.larry().value());
assert(&ta == obj1.larry().allocator());
assert(! obj1.hasCurly());
obj1.setCurly(10);
assert(5 == obj1.larry().value());
assert(&ta == obj1.larry().allocator());
assert(obj1.hasCurly());
assert(10 == obj1.curly().value());
assert(&ta == obj1.curly().allocator());
Definition bslma_testallocator.h:384

It may not be immediately obvious that getAdaptedAllocator provides much benefit; indeed, the example would work just fine if we called Larry::allocator() and passed the result directly to the constructor of Curly:

Larry larryObj(5, &ta);
Curly curlyObj(10, larryObj.allocator());
assert(&ta == curlyObj.allocator());
return 0;
}

The code above is brittle, however, as updating Larry to be bsl-AA would require calling larryObj.get_allocator().mechanism() instead of larryObj.allocator(). By usinggetAdaptedAllocator', the setCurly implementation above is robust in the face of such future evolution. This benefit is even more important in generic code.

Example 2: Retrieving a Specific Allocator Type from a Subobject

This example illustrates how bslma::AATypeUtil::getAllocatorFromSubobject can be used to retrieve an allocator of a specific type from a subobject even if that subobject uses an allocator with a smaller interface.

First, continuing from the previous example, we implement the get_allocator accessor. As we know, the allocator for a LarryMaybeCurly object is stored in the d_larry subobject, obviating a separate d_allocator_p member. However, the allocator within d_larry is a bslma::Allocator * whereas the allocator_type for LarryMaybeCurly is bsl::allocator<char>. When the LarryMaybeCurly object was constructed, some type information was lost in the conversion to bslma::Allocator. That information is recovered through the use of getAllocatorFromSubobject:

bsl::allocator<char> LarryMaybeCurly::get_allocator() const
{
typedef bslma::AATypeUtil Util;
return Util::getAllocatorFromSubobject<allocator_type>(d_larry);
}
Definition bslma_aatypeutil.h:302

Now we can construct a LarryMaybeCurly object with a specific allocator and recover that allocator using the get_allocator accessor:

int main()
{
bsl::allocator<char> bslAlloc(&ta);
LarryMaybeCurly obj1(5, bslAlloc);
assert(bslAlloc == obj1.get_allocator());

As in the previous example, it is possible to get the same effect without using the utilities in this component because bslma::Allocator * is implicitly convertible to bsl::allocator<char>:

Larry larryObj(5, &ta);
bsl::allocator<char> objAlloc = larryObj.allocator();
assert(objAlloc == bslAlloc);
return 0;
}
allocator()
Definition bslma_bslallocator.h:979

However, the preceding get_allocator implementation, like the setCurly implementation in the previous example, is more robust because it need not be changed if Larry is changed from legacy-AA to bsl-AA or if it is replaced by a template parameter that might use either AA model or even the pmr-AA model. Using the getAllocatorFromSubobject idiom is, in fact, vital to recovering bsl allocators stored within pmr-AA objects.