BDE 4.14.0 Production release
|
Provide methods to construct arbitrarily-typed objects uniformly.
This component provides a struct
, bslma::ConstructionUtil
, that serves as a namespace for utility functions to construct objects of an arbitrary (template parameter) type, given an allocator and an arbitrary number of arguments. These functions are useful for uniformly constructing an object without concern for whether the object's type is allocator-aware (AA) and, if so, whether the allocator is passed at the end of the argument list (trailing allocator-argument convention) or at the start of the argument list, preceeded by bsl::allocator_arg
(leading allocator-argument convention). If the type being constructed is not AA or if a non-allocator scalar such as an int
or void *
is passed to these methods, the allocator argument is discarded and a non-allocator constructor is invoked.
An additional destructiveMove
method moves an object to a new address and destroys the object at the original address, often doing so as an efficient memcpy
rather than separate move-destroy operations.
Legacy-AA (types with constructors having a parameter of type bslma::Allocator *
) and bsl-AA types (types with constructors having a parameter of type bsl::allocator<>
) are handled interchangeably by the functions in this component, accepting either a bslma::Allocator*
or a bsl::allocator
argument and then passing the appropriate type to the constructed type. Thus bsl-AA code can work seamlessly with legacy-AA code, allowing for a smooth transition from the old model to the new one. Note that this component does not directly use bsl::allocator
or bsl::polymorphic_allocator
because such use would cause a circular dependancy; an allocator is considered bsl-like if it is implicitly convertible from bslma::Allocator *
and has a mechanism
method that returns a bslma::Allocator *
and an allocator is considered pmr-like if it is convertible from bsl::memory_resource *
(and therefore also convertible from bslma::Allocator *
.
The construct
method provided here has roughly the same functionality as the C++20 library function std::uninitialized_construct_using_allocator
and the make
method has roughly the same functionality as std::make_obj_using_allocator
, with the following differences:
bslma::Allocator *
is treated as interchangeable with bsl::allocator
.int
or void *
) instead of an allocator to indicate that no allocator should be supplied to the object constructor, even if it is AA.std
functions will ignore the allocator if it is incompatible with the object type being constructed. The methods in this component, conversely, will ignore the allocator only for non-AA and stl-AA types. For legacy-AA, bsl-AA, and pmr-AA types, an incompatible allocator will result in a compilation failure. This special case exists to avoid subtle errors in the Bloomberg code base where a bad allocator argument is used in a situation where allocator propagation is expected.std::pair
.The facilities in this component query several type traits of the TYPE
being constructed, using that information as follows:
This section illustrates intended use of this component.
This example demonstrates the intended use of bslma::ConstructionUtil
to implement a simple container class that uses an instance of bsl::allocator
for memory management.
First, because allocation and construction are done in two separate steps, we need to define a proctor type that will deallocate the allocated memory in case the constructor throws an exception:
Then, we create a container class that holds a single element and uses bsl::allocator
to supply memory:
Next, we implement the private createElement
members that allocate memory and construct a TYPE
object in the allocated memory. We perform the allocation using the allocate
method of bsl::allocator
and the construction using the construct
method of ConstructionUtil
that provides the correct semantics for passing the allocator to the constructed object when appropriate:
Now, the destructor destroys the object and deallocates the memory used to hold the element using the allocator:
Next, the assignment operator needs to assign the value without modifying the allocator.
Finally, we perform a simple test of MyContainer
, instantiating it with element type int
:
This example demonstrates that MyContainer
does indeed propagate the allocator to its contained element.
First, we create a representative element class, MyType
. Unlike the MyContainer
template, MyType
allocates memory using the bslma::Allocator *
(legacy) allocator model instead of the bsl::allocator
(bsl) allocator model:
Finally, we instantiate MyContainer
using MyType
and verify that, when we provide an allocator to the constructor of the container, the same allocator is passed to the constructor of the contained element. Because the container and the element implement different allocator models, the invocation of bslma::ConstructionUtil::construct
automatically adapts the bsl::allocator
held by the container to a bslma::Allocator
pointer expected by the element. We also verify that, when the container is copy-constructed without supplying an allocator, the copy uses the default allocator, not the allocator from the original object. Moreover, we verify that the element stored in the copy also uses the default allocator:
This example demonstrates using bslma::ConstructionUtil::make
to implement a simple wrapper class that contains a single item that might or might not use the bslma
allocator protocol.
First, we define a wrapper class that holds an object and a functor. The functor (known as the listener) is called each time the wrapped object is changes. We store the object directly as a member variable, instead of using an uninitialized buffer, to avoid a separate construction step:
Next, we define the constructors such that they initialize d_value
using the specified allocator if and only if TYPE
accepts an allocator. The bslma::ConstructionUtil::make
family of functions encapsulate all of the metaprogramming that detects whether TYPE
uses an allocator and, if so, which construction protocol it uses (allocator at the front or at the back of the argument list), making all three constructors straightforward:
Note that, for d_value
to be constructed with the correct allocator, the compiler must construct the result returned from make
directly into the d_value
variable, an optimization known prior to C++17 as "copy elision". This optimization is required by the C++17 standard and is optional in pre-2017 standards, but is implemented in all of the C++11 compilers for which this component is expected to be used at Bloomberg.
Next, we implement the assignment operators, which simply call setValue
:
Then, we implement setValue
, which calls the listener after modifying the value:
Finally, we check our work by creating a listener for MyContainer<int>
that stores its last-seen value in a known location and a wrapper around MyContainer<int>
to test it: