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;
template <class TYPE, class ALLOC = bsl::allocator<TYPE> >
class MyContainer {
ALLOC d_allocator;
TYPE *d_value_p;
public:
typedef TYPE value_type;
typedef ALLOC allocator_type;
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();
ALLOC get_allocator() const { return d_allocator; }
TYPE& front() { return *d_value_p; }
const TYPE& front() const { return *d_value_p; }
Definition bslma_allocatortraits.h:1061
Next we define the type traits for MyContainer so that it is recognized as an STL sequence container:
- Defines STL iterators
- Is bitwise moveable if the allocator is bitwise moveable
- Uses
bslma allocators if the ALLOC template parameter is convertible from bslma::Allocator*.
MyContainer,
MyContainer,
};
#define BSLMF_NESTED_TRAIT_DECLARATION_IF(t_TYPE, t_TRAIT, t_COND)
Definition bslmf_nestedtraitdeclaration.h:243
Definition bslmf_isconvertible.h:867
Definition bslma_usesbslmaallocator.h:343
Definition bslmf_isbitwisemoveable.h:718
Then we implement the constructors, which allocate memory and construct a TYPE object in the allocated memory. Because the allocation and construction are done in two separate steps, we need to create a proctor that will deallocate the allocated memory in case the constructor throws an exception. The proctor uses the uniform interface provided by allocator_traits to access the pointer and deallocate members of ALLOC:
template <class ALLOC>
class MyContainerProctor {
ALLOC d_alloc;
pointer d_data_p;
public:
MyContainerProctor(const ALLOC& a, pointer p)
: d_alloc(a), d_data_p(p) { }
~MyContainerProctor() {
if (d_data_p) {
}
}
void release() { d_data_p = pointer(); }
};
BloombergLP::bslma::AllocatorTraits_PointerType< ALLOCATOR_TYPE >::type pointer
Definition bslma_allocatortraits.h:1149
static void deallocate(ALLOCATOR_TYPE &basicAllocator, pointer elementAddr, size_type n)
Definition bslma_allocatortraits.h:1458
Next, we perform the actual allocation and construction using the allocate and construct members of allocator_traits, which provide the correct semantic for passing the allocator to the constructed object when appropriate: template <class TYPE, class ALLOC>
MyContainer<TYPE, ALLOC>::MyContainer(const ALLOC& a)
: d_allocator(a)
{
d_value_p = AllocTraits::allocate(d_allocator, 1);
MyContainerProctor<ALLOC> proctor(a, d_value_p);
AllocTraits::construct(d_allocator, d_value_p);
proctor.release();
}
template <class TYPE, class ALLOC>
MyContainer<TYPE, ALLOC>::MyContainer(const TYPE& v, const ALLOC& a)
: d_allocator(a)
{
d_value_p = AllocTraits::allocate(d_allocator, 1);
MyContainerProctor<ALLOC> proctor(a, d_value_p);
AllocTraits::construct(d_allocator, d_value_p, v);
proctor.release();
}
Next, the copy constructor for MyContainer needs to conditionally copy the allocator from the other container. The copy constructor uses allocator_traits::select_on_container_copy_construction to decide whether to copy the other allocator (for non-bslma allocators) or to default-construct the allocator (for bslma allocators). template <class TYPE, class ALLOC>
MyContainer<TYPE, ALLOC>::MyContainer(const MyContainer& other)
: d_allocator(
bsl::allocator_traits<ALLOC>::
select_on_container_copy_construction(other.d_allocator))
{
d_value_p = AllocTraits::allocate(d_allocator, 1);
MyContainerProctor<ALLOC> proctor(d_allocator, d_value_p);
AllocTraits::construct(d_allocator, d_value_p, *other.d_value_p);
proctor.release();
}
Definition bdlb_printmethods.h:283
Now, the destructor uses allocator_traits functions to destroy and deallocate the value object: template <class TYPE, class ALLOC>
MyContainer<TYPE, ALLOC>::~MyContainer()
{
AllocTraits::destroy(d_allocator, d_value_p);
AllocTraits::deallocate(d_allocator, d_value_p, 1);
}
Finally, we perform a simple test of MyContainer, instantiating it with element type int: int usageExample1()
{
MyContainer<int> C1(123, &testAlloc);
assert(C1.front() == 123);
MyContainer<int> C2(C1);
assert(C2.front() == 123);
return 0;
}
Definition bslma_bslallocator.h:580
Definition bslma_testallocator.h:384
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:
class MyType {
public:
: d_allocator_p(
bslma::Default::allocator(basicAlloc)) { }
MyType(const MyType&)
: d_allocator_p(
bslma::Default::allocator(0)) { }
: d_allocator_p(
bslma::Default::allocator(basicAlloc)) { }
};
#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;
};
explicit MyCpp03Allocator(int state = 0) : d_state(state) { }
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); }
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))
}
template <class ELEMENT_TYPE, class A1, class A2>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
}
template <class ELEMENT_TYPE, class A1, class A2, class A3>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
}
template <class ELEMENT_TYPE, class A1, class A2, class A3, class A4>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
}
template <class ELEMENT_TYPE,
class A1,
class A2,
class A3,
class A4,
class A5>
void construct(ELEMENT_TYPE *p,
{
::new (static_cast<void *>(p))
}
template <class ELEMENT_TYPE>
void destroy(ELEMENT_TYPE *p) { p->~ELEMENT_TYPE(); }
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));
MyContainer<MyType, MyTypeAlloc> C2a(C1a);
assert(C2a.get_allocator() == C1a.get_allocator());
assert(C2a.get_allocator() != MyTypeAlloc());
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));
MyContainer<MyType, MyTypeAlloc> C2b(C1b);
assert(C2b.get_allocator() == C1b.get_allocator());
assert(C2b.get_allocator() != MyTypeAlloc());
return 0;
}
Definition bslmf_issame.h:146
static Allocator * defaultAllocator()
Definition bslma_default.h:889