BDE 4.14.0 Production release
|
Provide metafunctions to detect a type's allocator-awareness model.
struct
representing no AA modelpolymorphic_allocator
)bsl::allocator
)bslma::Allocator*
)T
supports model M
T
This component provides five tag struct
s (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.
The AA model supported by a type T
is determined primarily by the type of allocator used by T
to allocate memory:
T
does not use an externally supplied allocator, then it is not AA.T
uses the address of a bslma::Allocator
, then it is legacy-AA. Most older AA classes developed at Bloomberg are legacy-AA. The value of bslma::UsesBslmaAllocator<T>::value
must be true for a type to be detected as supporting the legacy-AA model.T
uses an instantiation of bsl::allocator
, then it is bsl-AA. Most newer AA classes developed at Bloomberg are bsl-AA. T
is automatically detectable as supporting the bsl-AA model if there exists a nested type T::allocator_type
that is convertible from bsl::allocator<char>
.T
uses an instantiation of bsl::polymorphic_allocator
, then it is pmr-AA. T
will be automatically detectable as supporting the pmr-AA model if there exists a nested type T::allocator_type
that is convertible from bsl::polymorphic_allocator<char>
.T
uses an STL-style allocator other than bsl::allocator
or bsl::polymorphic_allocator
, then it is stl-AA. For the purposes of this component, any class having an allocator_type
member that is not convertible from bsl::allocator
or bsl::polymorphic_allocator
is assumed to be fall into this category. For example, bslstl::vector
is stl-AA if it is instantiated with a third-party STL-compliant allocator.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.
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:
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.
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:
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:
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.
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:
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
:
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:
Now, to see the effect of these constructors, we'll use a simple AA class, SampleAAType
that does nothing more than hold the 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:
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
.
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:
TYPE
is bsl-AA, return obj.get_allocator()
.TYPE
is legacy-AA, return bsl::allocator<char>(obj.allocator())
.TYPE
is not AA, return bsl::allocator<char>()
.TYPE
is AA but not one of the above, compilation will fail.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:
Next, we dispatch to one of the implementation functions using AAModel<TYPE>
to yield a tag that indicates the AA model used by TYPE
.
Now, to check all of the possibilities, we create a minimal AA type sporting the legacy-AA interface:
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.