Provide compile-time traits that can be associated with types.
More...
Defines |
#define | BSLALG_DECLARE_NESTED_TRAITS(T, TRAIT) |
#define | BSLALG_DECLARE_NESTED_TRAITS2(T, TRAIT1, TRAIT2) |
#define | BSLALG_DECLARE_NESTED_TRAITS3(T, TRAIT1, TRAIT2, TRAIT3) |
#define | BSLALG_DECLARE_NESTED_TRAITS4(T, TRAIT1, TRAIT2, TRAIT3, TRAIT4) |
#define | BSLALG_DECLARE_NESTED_TRAITS5(T, TRAIT1, TRAIT2, TRAIT3, TRAIT4, TRAIT5) |
#define | BDEALG_DECLARE_NESTED_TRAITS(T, TRAITS) BSLALG_DECLARE_NESTED_TRAITS(T, TRAITS) |
#define | BDEALG_DECLARE_NESTED_TRAITS2(T, TRAIT1, TRAIT2) BSLALG_DECLARE_NESTED_TRAITS2(T, TRAIT1, TRAIT2) |
#define | BDEALG_DECLARE_NESTED_TRAITS3(T, TRAIT1, TRAIT2, TRAIT3) BSLALG_DECLARE_NESTED_TRAITS3(T, TRAIT1, TRAIT2, TRAIT3) |
#define | BDEALG_DECLARE_NESTED_TRAITS4(T, TRAIT1, TRAIT2, TRAIT3, TRAIT4) BSLALG_DECLARE_NESTED_TRAITS4(T, TRAIT1, TRAIT2, TRAIT3, TRAIT4) |
#define | BDEALG_DECLARE_NESTED_TRAITS5(T, TRAIT1, TRAIT2, TRAIT3, TRAIT4, TRAIT5) BSLALG_DECLARE_NESTED_TRAITS5(T, TRAIT1,TRAIT2,TRAIT3,TRAIT4,TRAIT5) |
#define | bdealg_HasTrait bslalg::HasTrait |
#define | bdealg_SelectTrait bslalg::SelectTrait |
Detailed Description
- Outline
-
-
- Purpose:
- Provide compile-time traits that can be associated with types.
- Deprecated:
- Use bslmf_nestedtraitdeclaration instead.
-
- Classes:
-
-
- Macros:
BSLALG_DECLARE_NESTED_TRAITS | declares a trait in a nested fashion |
BSLALG_DECLARE_NESTED_TRAITS2 | declares two traits in a nested fashion |
BSLALG_DECLARE_NESTED_TRAITS3 | declares three traits in a nested fashion |
BSLALG_DECLARE_NESTED_TRAITS4 | declares four traits in a nested fashion |
BSLALG_DECLARE_NESTED_TRAITS5 | declares five traits in a nested fashion |
- See also:
- Component bslalg_constructorproxy, Component bslalg_scalarprimitives
-
- Description:
- This component provides a set of type traits, and a set of macros used to assign traits to user-defined classes. Traits are used to enable certain optimizations or discriminations at compile-time. For instance, a class having the trait
bslalg::TypeTraitBitwiseMoveable
may allow resizing an array of objects by simply calling std::memcpy
instead of invoking a copy-constructor on every object. The usage example shows how to use the bslalg::TypeTraitUsesBslmaAllocator
to propagate allocators to nested objects that may require them.
- This component should be used in conjunction with other components from the package
bslalg
. See the package-level documentation for an overview. The most useful classes and macros defined in this component are:
-
- Usage:
- In this usage example, we show how to enable the
bslma
allocator model for generic containers, by implementing simplified versions of the bslalg_constructorproxy
and bslalg_scalarprimitives
components. The interested reader should refer to the documentation of those components.
-
- A Generic Container:
- Suppose we want to implement a generic container of a parameterized
TYPE
, which may or may not follow the bslma
allocator model. If it does, our container should pass an extra bslma::Allocator*
argument to copy construct a value; but if it does not, then passing this extra argument is going to generate a compile-time error. It thus appears we need two implementations of our container. This can be done more succinctly by encapsulating into the constructor some utilities that will, through a single interface, determine whether TYPE
has the trait bslalg::TypeTraitUsesBslmaAllocator
and copy-construct it accordingly.
- The container contains a single data member of the parameterized
TYPE
. Since we are going to initialize this data member manually, we do not want it to be automatically constructed by the compiler. For this reason, we encapsulate it in a bsls::ObjectBuffer
. Since the container offers a uniform interface that always takes an extra allocator argument, regardless of whether TYPE
does or not, we can declare it to have the bslalg::TypeTraitUsesBslmaAllocator
trait: For simplicity, we let the container contain only a single element, and require that an element always be initialized.
explicit MyGenericContainer(const TYPE& object,
bslma::Allocator *allocator = 0);
MyGenericContainer(const MyGenericContainer& container,
bslma::Allocator *allocator = 0);
~MyGenericContainer();
We can also allow the container to change the object it contains, by granting modifiable as well as non-modifiable access to this object:
TYPE& object();
const TYPE& object() const;
};
-
- Using the Type Traits:
- The challenge in the implementation lies in using the traits of the contained
TYPE
to determine whether to pass the allocator argument to its copy constructor. We rely here on a property of templates that templates are not compiled (and thus will not generate compilation errors) until they are instantiated. Hence, we can use two function templates, and let the overloading resolution (based on the nested traits) decide which to instantiate. The generic way to create an object, passing through all arguments (value and allocator) is as follows. For brevity and to avoid breaking the flow of this example, we have embedded the function definition into the class. For types that don't use an allocator, we offer the following overload that will be selected if the type trait of TYPE
cannot be converted to bslalg::TypeTraitUsesBslmaAllocator
. In that case, note that the type traits always inherit from bslalg::TypeTraitNil
. And finally, this function will instantiate the type trait and pass it to the appropriately (compiler-)chosen overload:
-
- Generic Container Implementation:
- With these utilities, we can now implement
MyGenericContainer
.
template <typename TYPE>
MyGenericContainer<TYPE>::MyGenericContainer(const TYPE& object,
bslma::Allocator *allocator)
{
my_GenericContainerUtil::copyConstruct(&d_object.object(),
object,
allocator);
}
template <typename TYPE>
MyGenericContainer<TYPE>::MyGenericContainer(
const MyGenericContainer& container,
bslma::Allocator *allocator)
{
my_GenericContainerUtil::copyConstruct(&d_object.object(),
container.object(),
allocator);
}
Note that all this machinery only affects the constructors, and not the destructor which only invokes the destructor of d_object
. template <typename TYPE>
MyGenericContainer<TYPE>::~MyGenericContainer()
{
(&d_object.object())->~TYPE();
}
To finish, the accessors and manipulators are trivially implemented.
template <typename TYPE>
TYPE& MyGenericContainer<TYPE>::object()
{
return d_object.object();
}
template <typename TYPE>
const TYPE& MyGenericContainer<TYPE>::object() const
{
return d_object.object();
}
-
- Usage Verification:
- We can check that our container actually forwards the correct allocator to its contained objects with a very simple test apparatus, consisting of two classes that have exactly the same signature and implementation except that one has the
bslalg::TypeTraitUsesBslmaAllocator
trait and the other does not: bslma::Allocator *allocSlot;
struct MyTestTypeWithBslmaAllocatorTraits {
BSLALG_DECLARE_NESTED_TRAITS(MyTestTypeWithBslmaAllocatorTraits,
BloombergLP::bslalg::TypeTraitUsesBslmaAllocator);
MyTestTypeWithBslmaAllocatorTraits() {}
MyTestTypeWithBslmaAllocatorTraits(
const MyTestTypeWithBslmaAllocatorTraits&,
bslma::Allocator *allocator)
{
allocSlot = allocator;
}
};
struct MyTestTypeWithNoBslmaAllocatorTraits {
MyTestTypeWithNoBslmaAllocatorTraits() {}
MyTestTypeWithNoBslmaAllocatorTraits(
const MyTestTypeWithNoBslmaAllocatorTraits &,
bslma::Allocator *allocator)
{
allocSlot = allocator;
}
};
Our verification program simply instantiates several MyGenericContainer
templates with the two test types above, and checks that the allocator slot is as expected: With MyTestTypeWithNoBslmaAllocatorTraits
, the slot should never be set. MyTestTypeWithNoBslmaAllocatorTraits x;
allocSlot = &ta0;
MyGenericContainer<MyTestTypeWithNoBslmaAllocatorTraits> x0(x);
assert(&ta0 == allocSlot);
allocSlot = &ta0;
MyGenericContainer<MyTestTypeWithNoBslmaAllocatorTraits> x1(x, &ta1);
assert(&ta0 == allocSlot);
With MyTestTypeWithBslmaAllocatorTraits
, the slot should be set to the allocator argument, or to 0 if not specified: MyTestTypeWithBslmaAllocatorTraits y;
allocSlot = &ta0;
MyGenericContainer<MyTestTypeWithBslmaAllocatorTraits> y0(y);
assert(0 == allocSlot);
allocSlot = &ta0;
MyGenericContainer<MyTestTypeWithBslmaAllocatorTraits> y1(y, &ta1);
assert(&ta1 == allocSlot);
return 0;
}
Define Documentation
#define BSLALG_DECLARE_NESTED_TRAITS |
( |
|
T, |
|
|
|
TRAIT | |
|
) |
| | |
Value:operator TRAIT::NestedTraitDeclaration<T>() const { \
return TRAIT::NestedTraitDeclaration<T>(); \
}
#define BSLALG_DECLARE_NESTED_TRAITS2 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2 | |
|
) |
| | |
#define BSLALG_DECLARE_NESTED_TRAITS3 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2, |
|
|
|
TRAIT3 | |
|
) |
| | |
#define BSLALG_DECLARE_NESTED_TRAITS4 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2, |
|
|
|
TRAIT3, |
|
|
|
TRAIT4 | |
|
) |
| | |
#define BSLALG_DECLARE_NESTED_TRAITS5 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2, |
|
|
|
TRAIT3, |
|
|
|
TRAIT4, |
|
|
|
TRAIT5 | |
|
) |
| | |
#define BDEALG_DECLARE_NESTED_TRAITS |
( |
|
T, |
|
|
|
TRAITS | |
|
) |
| | BSLALG_DECLARE_NESTED_TRAITS(T, TRAITS) |
#define BDEALG_DECLARE_NESTED_TRAITS2 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2 | |
|
) |
| | BSLALG_DECLARE_NESTED_TRAITS2(T, TRAIT1, TRAIT2) |
#define BDEALG_DECLARE_NESTED_TRAITS3 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2, |
|
|
|
TRAIT3 | |
|
) |
| | BSLALG_DECLARE_NESTED_TRAITS3(T, TRAIT1, TRAIT2, TRAIT3) |
#define BDEALG_DECLARE_NESTED_TRAITS4 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2, |
|
|
|
TRAIT3, |
|
|
|
TRAIT4 | |
|
) |
| | BSLALG_DECLARE_NESTED_TRAITS4(T, TRAIT1, TRAIT2, TRAIT3, TRAIT4) |
#define BDEALG_DECLARE_NESTED_TRAITS5 |
( |
|
T, |
|
|
|
TRAIT1, |
|
|
|
TRAIT2, |
|
|
|
TRAIT3, |
|
|
|
TRAIT4, |
|
|
|
TRAIT5 | |
|
) |
| | BSLALG_DECLARE_NESTED_TRAITS5(T, TRAIT1,TRAIT2,TRAIT3,TRAIT4,TRAIT5) |