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

Detailed Description

Outline

Purpose

Provide metafunctions to detect a type's allocator-awareness model.

Classes

See also
bslma_allocator, bslma_bslallocator

Description

This component provides five tag structs (AAModelNone, AAModelLegacy, AAModelBsl, AAModelPmr, and AAModelStl) and a pair of metafunction classes (AAModelIsSupported<TYPE, MODEL> and AAModel<TYPE>) that allow generic allocator-handling facilities – such as those that construct allocator-aware (a.k.a., AA) objects, extract the allocator from AA objects, and manage propagation of allocators from a container to its contained elements – to determine, at compile time, the AA model of the type(s) they work with.

Supported Allocator-Aware (AA) models

The AA model supported by a type T is determined primarily by the type of allocator used by T to allocate memory:

Note that a single type can support more than one AA model. For example, any type that is bsl-AA can be constructed with a bsl::Allocator pointer and, thus, can be said to support the legacy-AA model as well. Similarly, all AA types are assumed to be constructible without specifying an allocator (typically using a default-initialized allocator value) and, thus, can be used as non-AA types.

Allocator-Aware (AA) model tags

Each of the above models has a corresponding model tag struct in this component. A model tag can be thought of as an enumerator except that, rather than being a simple integral value within an enum, each one is a struct that has no non-static data members, is derived from an instantiation of bsl::integral_constant, and has a type member typedef alias for itself. By representing each model as a separate type rather than a simple value, these tag types are more versatile when used for metaprogramming. The table below lists the value constant for each model tag struct:

+---------------+-------+
| model tag | value |
+---------------+-------+
| AAModelNone | 0 |
| AAModelPmr | 1 |
| AAModelBsl | 2 |
| AAModelLegacy | 3 |
| AAModelStl | 4 |
+---------------+-------+

Note that these values are roughly in order of "pickyness" of the model: A legacy-AA type's constructors accept only allocators convertible to bslma::Allocator * whereas a pmr-AA type is less picky in that its constructors also accept allocators convertible to bsl::polymorphic_allocator (including bsl::allocator). The models are not fully ordered in that, for example, stl-AA is orthogonal to the rest and is neither more nor less picky than legacy-AA.

AA-model metafunctions

The metafunctions AAModel<T> and AAModelIsSupported<T,M> are used for compile-time dispatch of generic code for different allocator models. This subsection describes the criteria by which these metafunctions determine the AA model for a type T. The information here is not critical for understanding the use of this component, so readers are welcome to skip forward to the usage examples, below.

With the exception of legacy-AA, AA types generally adhere to the basic interface pattern shown here:

class AAType {
// ... (private members)
public:
// TYPES
typedef some-type allocator_type;
// CREATORS
// ... (constructors that optionally take an 'allocator_type' argument)
~AAType();
// MANIPULATORS
// ...
// ACCESSORS
// ...
allocator_type get_allocator() const;
};

The metafunctions in this component test for the existence of the allocator_type member. If T::allocator_type exists, then T is assumed to support the stl-AA model. If, additionally, bsl::polymorphic_allocator<char> and/or bsl::allocator<char> are convertible to T::allocator_type then T supports the pmr-AA and/or bsl-AA models, respectively. The metafunctions do not require that AA constructors exist nor that the get_allocator member function exists.

The interface for a legacy-AA type is somewhat different:

class LegacyAAType {
// ... (private members)
public:
// TRAITS
// CREATORS
// ... (constructors that optionally take an 'bslma::Allocator*' arg)
~LegacyAAType();
// MANIPULATORS
// ...
// ACCESSORS
// ...
bslma::Allocator *allocator() const;
};
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bslma_allocator.h:457
Definition bslma_usesbslmaallocator.h:343

If bslma::UsesBslmaAllocator<T>::value is true, then the metafunctions assume that T supports the legacy-AA model. Again, the presence of appropriate allocators or the allocator accessor are not required.

Usage

Example 1: Conditionally Passing an Allocator to a Constructor

This example demonstrates the use of AAModelIsSupported to choose an appropriate overload for AA constructors. Consider a bsl-AA class, Wrapper, that wraps an object of template-parameter type, TYPE. First, we define the data members:

/// Wrap an object of type `TYPE`.
template <class TYPE>
class Wrapper {
// DATA
bsl::allocator<char> d_allocator;
TYPE d_object;
public:
// TYPES
typedef bsl::allocator<char> allocator_type;
Definition bslma_bslallocator.h:580

Next, we define the constructors. The constructors for Wrapper would all take an optional allocator_type argument, but the d_object member might or might not be constructed with an allocator argument. To handle the allocator correctly, therefore, we choose to have two versions of each constructor: one that is invoked if TYPE is AA and one that is invoked if it is not. Since both constructors have the same argument list, we must make them templates and distinguish them using SFINAE so that only one instantiation is valid, i.e., by using enable_if along with the AAModelIsSupported:

// CREATORS
/// Construct a `Wrapper` using the specified `a` allocator, passing
/// the allocator to the wrapped object. This constructor will not
/// participate in overload resolution unless `TYPE` supports the
/// legacy allocator-awareness model (**legacy-AA**).
template <class ALLOC>
explicit
Wrapper(const ALLOC& a,
typename bsl::enable_if<
int
>::type = 0)
: d_allocator(a), d_object(d_allocator.mechanism()) { }
/// Construct a `Wrapper` using the specified `a` allocator,
/// constructing the wrapped object without an explicit allocator.
/// This constructor will not participate in overload resolution if
/// `TYPE` supports the legacy allocator-awareness model
/// (**legacy-AA**).
template <class ALLOC>
explicit
Wrapper(const ALLOC& a,
typename bsl::enable_if<
int
>::type = 0)
: d_allocator(a), d_object() { }
Definition bslmf_enableif.h:525
Definition bslmf_isconvertible.h:867
Definition bslma_aamodel.h:542

Support for bsl-AA implies support for legacy-AA, so the example above needs to test for only the latter model; the first constructor overload is selected if TYPE implements either AA model. Similarly d_allocator.mechanism() yields a common denominator type, bslma::Allocator * that can be passed to the constructor for d_object, regardless of its preferred AA model. The second overload is selected for types that do not support the legacy-AA (or bsl-AA) model. Note that this example, though functional, does not handle all cases; e.g., it does not handle types whose allocator constructor parameter is preceded by bsl::allocator_arg_t. See higher-level components such as bslma_contructionutil for a more comprehensive treatment of AA constructor variations.

Next, we finish up our class by creating accessors to get the allocator and wrapped object:

// ACCESSORS
/// Return the allocator used to construct this object.
const allocator_type get_allocator() const { return d_allocator; }
const TYPE& value() const { return d_object; }
};

Now, to see the effect of these constructors, we'll use a simple AA class, SampleAAType that does nothing more than hold the allocator:

/// Sample AA class that adheres to the bsl-AA interface.
class SampleAAType {
// DATA
bsl::allocator<char> d_allocator;
public:
// TYPES
typedef bsl::allocator<char> allocator_type;
// CREATORS
explicit SampleAAType(const allocator_type& alloc = allocator_type())
: d_allocator(alloc) { }
SampleAAType(const SampleAAType&) { }
// MANIPULATORS
SampleAAType& operator=(const SampleAAType&) { return *this; }
// ACCESSORS
allocator_type get_allocator() const { return d_allocator; }
};

Finally, in our main program, create an allocator and pass it to a couple of Wrapper objects, one instantiated with int and the other instantiated with our SampleAAType. We verify that both were constructed appropriately, with the allocator being used by the SampleAAType object, as desired:

void main()
{
Wrapper<int> w1(&alloc);
assert(&alloc == w1.get_allocator());
assert(0 == w1.value());
Wrapper<SampleAAType> w2(&alloc);
assert(&alloc == w2.get_allocator());
assert(&alloc == w2.value().get_allocator());
}
Definition bslma_testallocator.h:384

Note that, even though SampleAAType conforms to the bsl-AA interface, it is also supports the legacy-AA model because bslma::Allocator * is convertible to bsl::allocator.

Example 2: Choose an Implementation Based on Allocator-Aware (AA) model

This example demonstrates the use of AAModel to dispatch among several implementations based on the AA model preferred by a parameter type. We would like a uniform way to get the allocator used by an object. We'll define a utility class, Util, containing a static member function template, getAllocator(const TYPE& obj) returning a bsl::allocator<char> as follows:

We'll use AAModel<TYPE> to dispatch to one of three implementations of getAllocator.

First, we declare the Util class and three private overloaded implemention functions, each taking an argument of a different AA model tag:

/// Namespace for functions that operate on AA types.
class Util {
template <class TYPE>
static bsl::allocator<char> getAllocatorImp(const TYPE& obj,
{ return obj.get_allocator(); }
template <class TYPE>
static bsl::allocator<char> getAllocatorImp(const TYPE& obj,
{ return obj.allocator(); }
template <class TYPE>
static bsl::allocator<char> getAllocatorImp(const TYPE&,
{ return bsl::allocator<char>(); }
allocator()
Definition bslma_bslallocator.h:979
Model tag for bsl-AA types.
Definition bslma_aamodel.h:482
Model tag for legacy-AA types.
Definition bslma_aamodel.h:493
Model tag for non-AA types.
Definition bslma_aamodel.h:460

Next, we dispatch to one of the implementation functions using AAModel<TYPE> to yield a tag that indicates the AA model used by TYPE.

public:
// CLASS METHODS
template <class TYPE>
static bsl::allocator<char> getAllocator(const TYPE& obj)
{ return getAllocatorImp(obj, bslma::AAModel<TYPE>()); }
};
Definition bslma_aamodel.h:525

Now, to check all of the possibilities, we create a minimal AA type sporting the legacy-AA interface:

/// Sample AA class that adheres to the bsl-AA interface.
class SampleLegacyAAType {
// DATA
bslma::Allocator *d_allocator_p;
public:
// TRAITS
BSLMF_NESTED_TRAIT_DECLARATION(SampleLegacyAAType,
// CREATORS
explicit SampleLegacyAAType(bslma::Allocator *a =0)
: d_allocator_p(a) { }
// ACCESSORS
bslma::Allocator *allocator() const { return d_allocator_p; }
};

Finally, we create objects of SampleAAType and SampleLegacyAAType using different allocators as well as an object of type float (which, of course is not AA), and verify that Util::getAllocator returns the correct allocator for each.

void main()
{
SampleAAType obj1(&ta1);
SampleLegacyAAType obj2(&ta2);
float obj3 = 0.0;
assert(Util::getAllocator(obj1) == &ta1);
assert(Util::getAllocator(obj2) == &ta2);
assert(Util::getAllocator(obj3) == bslma::Default::defaultAllocator());
}
static Allocator * defaultAllocator()
Definition bslma_default.h:889