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

Detailed Description

Outline

Purpose

Provide a uniform interface to standard allocator types.

Classes

See also
bslma_allocator, bslma_bslallocator

TBD: update component-level doc

Description

The standard allocator_traits class template is defined in the C++11 standard ([allocator.traits]) as a uniform mechanism for accessing nested types within, and operations on, any standard-conforming allocator. An allocator_traits specialization is stateless, and all of its member functions are static. In most cases, facilities of allocator_traits are straight pass-throughs for the same facilities from the ALLOC template parameter. For example, allocator_traits<X>::pointer is the same as X::pointer and allocator_traits<X>::allocate(x, n) is the same as x.allocate(n). The advantage of using allocator_traits instead of directly using the allocator is that the allocator_traits interface can supply parts of the interface that are missing from ALLOC. In fact, the most important purpose of allocator_traits is to provide implementations of C++11 allocator features that were absent in C++03, thus allowing a C++03 allocator to work with C++11 containers.

This component provides a full C++11 interface for allocator_traits, but constrains the set of allocator types on which it may be instantiated. Specifically, this implementation does not provide defaults for C++03 types and functions, and has hard-wired implementations of the new C++11 features. Thus, the allocator_traits template cannot be instantiated on an allocator type that does not provide a full compliment of types and functions required by the C++03 standard, and it will ignore any special C++11 features specified in ALLOC. This limitation exists because Bloomberg does not need the full functionality of the C++11 model, but needs only to distinguish between C++03 allocators and allocators that implement the BSLMA allocator model (see bslma_bslallocator ). The full feature set of allocator_traits would require a lot of resources for implementation and (especially) testing. Moreover, a full implementation would require metaprogramming that is too advanced for the feature set of the compilers currently in use at Bloomberg. This interface is useful, however, as a way to future-proof containers against the eventual implementation of the full feature set, and to take advantage of the Bloomberg-specific features described below.

There are two important (new) C++11 features provided by the allocator_traits interface: the construct function having a variable-length argument list (limited to 5 constructor arguments on compilers that don't support variadic templates) and the allocator-propagation traits. The implementations of these features within this component are tuned to Bloomberg's needs. The construct member function will automatically forward the allocator to the constructed object iff the ALLOC parameter is convertible from bslma::Allocator* and the object being constructed has the bslma::UsesBslmaAllocator type trait, as per standard Bloomberg practice. The select_on_container_copy_construction static member will return a default-constructed allocator iff ALLOC is convertible from bslma::Allocator * because bslma allocators should not be copied when a container is copy-constructed; otherwise this function will return a copy of the allocator, as per C++03 container rules. The other propagation traits all have a false value, so allocators are not propagated on assignment or swap.

Note that use of this component will differ from a strict following of the C++03 standard, as the construct and destroy methods of the parameterized allocator type will not be called. Rather, the target object will always be constructed at the address specified by the user, by calling the constructor in-place. Similarly, the destructor will always be called directly, rather than using a parameterized allocator's destroy method. Otherwise, this implementation will fully support the C++03 model, including use of allocators returning "smart pointers" from allocate.

Usage

In this section we show intended usage of this component.

Example 1: A Container Class

This example demonstrates the intended use of allocator_traits to implement a standard-conforming container class. First, we create a container class that holds a single object and which meets the requirements both of a standard container and of a Bloomberg container. I.e., when instantiated with an allocator argument it uses the standard allocator model; otherwise it uses the bslma model. We provide an alias, AllocTraits, to the specific allocator_traits instantiation to simplify the implementation of each method that must allocate memory, or create or destroy elements.

using namespace BloombergLP;
/// This class provides a container that always holds exactly one
/// element, dynamically allocated using the specified allocator.
template <class TYPE, class ALLOC = bsl::allocator<TYPE> >
class MyContainer {
/// Alias for the `allocator_traits` instantiation to use for all
/// memory management requests.
typedef bsl::allocator_traits<ALLOC> AllocTraits;
// DATA
ALLOC d_allocator;
TYPE *d_value_p;
public:
typedef TYPE value_type;
typedef ALLOC allocator_type;
// etc.
// CREATORS
explicit MyContainer(const ALLOC& a = ALLOC());
explicit MyContainer(const TYPE& v, const ALLOC& a = ALLOC());
MyContainer(const MyContainer& other);
MyContainer(const MyContainer& other, const ALLOC& a);
~MyContainer();
// MANIPULATORS
ALLOC get_allocator() const { return d_allocator; }
// ACCESSORS
TYPE& front() { return *d_value_p; }
const TYPE& front() const { return *d_value_p; }
// etc.
Definition bslma_allocatortraits.h:1061

Next we define the type traits for MyContainer so that it is recognized as an STL sequence container:

Example 2: C++03 Allocators

This example shows that when MyContainer is instantiated with a C++03 allocator, that the allocator is a) copied on copy construction and b) is not propagated from the container to its elements. Firstly we create a representative element class, MyType, that allocates memory using the bslma allocator protocol:

#include <bslma_default.h>
class MyType {
bslma::Allocator *d_allocator_p;
// etc.
public:
// TRAITS
// CREATORS
explicit MyType(bslma::Allocator* basicAlloc = 0)
: d_allocator_p(bslma::Default::allocator(basicAlloc)) { /* ... */ }
MyType(const MyType&)
: d_allocator_p(bslma::Default::allocator(0)) { /* ... */ }
MyType(const MyType&, bslma::Allocator* basicAlloc)
: d_allocator_p(bslma::Default::allocator(basicAlloc)) { /* ... */ }
// etc.
// ACCESSORS
bslma::Allocator *allocator() const { return d_allocator_p; }
// etc.
};
#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)
Definition bslmf_nestedtraitdeclaration.h:231
Definition bslma_allocator.h:457
Definition balxml_encoderoptions.h:68

Then we create a C++03-style allocator class template:

template <class TYPE>
class MyCpp03Allocator {
int d_state;
public:
typedef TYPE value_type;
typedef TYPE *pointer;
typedef const TYPE *const_pointer;
typedef unsigned size_type;
typedef int difference_type;
template <class OTHER>
struct rebind {
typedef MyCpp03Allocator<OTHER> other;
};
// CREATORS
explicit MyCpp03Allocator(int state = 0) : d_state(state) { }
// ALLOCATION FUNCTIONS
TYPE* allocate(size_type n, const void* = 0)
{ return static_cast<TYPE *>(::operator new(sizeof(TYPE) * n)); }
void deallocate(TYPE* p, size_type) { ::operator delete(p); }
// ELEMENT CREATION FUNCTIONS
template <class ELEMENT_TYPE>
void construct(ELEMENT_TYPE *p)
{
::new (static_cast<void *>(p)) ELEMENT_TYPE();
}
template <class ELEMENT_TYPE, class A1>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1));
}
template <class ELEMENT_TYPE, class A1, class A2>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
}
template <class ELEMENT_TYPE, class A1, class A2, class A3>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
}
template <class ELEMENT_TYPE, class A1, class A2, class A3, class A4>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
}
template <class ELEMENT_TYPE,
class A1,
class A2,
class A3,
class A4,
class A5>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
}
template <class ELEMENT_TYPE>
void destroy(ELEMENT_TYPE *p) { p->~ELEMENT_TYPE(); }
// ACCESSORS
static size_type max_size() { return UINT_MAX / sizeof(TYPE); }
int state() const { return d_state; }
};
template <class TYPE1, class TYPE2>
inline
bool operator==(const MyCpp03Allocator<TYPE1>& lhs,
const MyCpp03Allocator<TYPE2>& rhs)
{
return lhs.state() == rhs.state();
}
template <class TYPE1, class TYPE2>
inline
bool operator!=(const MyCpp03Allocator<TYPE1>& lhs,
const MyCpp03Allocator<TYPE2>& rhs)
{
return ! (lhs == rhs);
}
#define BSLS_COMPILERFEATURES_FORWARD_REF(T)
Definition bsls_compilerfeatures.h:2012
#define BSLS_COMPILERFEATURES_FORWARD(T, V)
Definition bsls_compilerfeatures.h:2018
bool operator!=(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
bool operator==(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)

Finally we instantiate MyContainer using this allocator type and verify that elements are constructed using the default allocator (because the allocator is not propagated from the container). We also verify that the allocator is copied on copy-construction:

int usageExample2()
{
typedef MyCpp03Allocator<MyType> MyTypeAlloc;
MyContainer<MyType, MyTypeAlloc> C1a(MyTypeAlloc(1));
assert((bsl::is_same<MyContainer<MyType, MyTypeAlloc>::allocator_type,
MyTypeAlloc>::value));
assert(C1a.get_allocator() == MyTypeAlloc(1));
assert(C1a.front().allocator() == bslma::Default::defaultAllocator());
MyContainer<MyType, MyTypeAlloc> C2a(C1a);
assert(C2a.get_allocator() == C1a.get_allocator());
assert(C2a.get_allocator() != MyTypeAlloc());
assert(C2a.front().allocator() == bslma::Default::defaultAllocator());
MyType dummy;
MyContainer<MyType, MyTypeAlloc> C1b(dummy, MyTypeAlloc(1));
assert((bsl::is_same<MyContainer<MyType, MyTypeAlloc>::allocator_type,
MyTypeAlloc>::value));
assert(C1b.get_allocator() == MyTypeAlloc(1));
assert(C1b.front().allocator() == bslma::Default::defaultAllocator());
MyContainer<MyType, MyTypeAlloc> C2b(C1b);
assert(C2b.get_allocator() == C1b.get_allocator());
assert(C2b.get_allocator() != MyTypeAlloc());
assert(C2b.front().allocator() == bslma::Default::defaultAllocator());
return 0;
}
Definition bslmf_issame.h:146
static Allocator * defaultAllocator()
Definition bslma_default.h:889