|
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 MTThis 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.
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.