BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmf_allocatorargt.h
Go to the documentation of this file.
1/// @file bslmf_allocatorargt.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmf_allocatorargt.h -*-C++-*-
8#ifndef INCLUDED_BSLMF_ALLOCATORARGT
9#define INCLUDED_BSLMF_ALLOCATORARGT
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmf_allocatorargt bslmf_allocatorargt
15/// @brief Provide a tag type to precede allocator arguments.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmf
19/// @{
20/// @addtogroup bslmf_allocatorargt
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmf_allocatorargt-purpose"> Purpose</a>
25/// * <a href="#bslmf_allocatorargt-classes"> Classes </a>
26/// * <a href="#bslmf_allocatorargt-description"> Description </a>
27/// * <a href="#bslmf_allocatorargt-usage"> Usage </a>
28/// * <a href="#bslmf_allocatorargt-example-1-disambiguate-a-constructor-invocation"> Example 1: Disambiguate a constructor invocation </a>
29///
30/// # Purpose {#bslmf_allocatorargt-purpose}
31/// Provide a tag type to precede allocator arguments.
32///
33/// # Classes {#bslmf_allocatorargt-classes}
34///
35/// - bsl::allocator_arg_t: tag indicating the next parameter is an allocator
36///
37/// @see bslalg_scalarprimitives
38///
39/// # Description {#bslmf_allocatorargt-description}
40/// The C++11 standard defines the empty class
41/// `bsl::allocator_arg_t` as a tag that precedes an argument of allocator type
42/// in circumstances where context alone cannot be used to determine which
43/// argument is an allocator. Typically, this disambiguation is needed when a
44/// class has templated constructors taking variable numbers of arguments, each
45/// of which is of parameterized type. If that class also uses an allocator (of
46/// either STL style or `bslma`/`memory_resource` style), then the allocator
47/// argument must be flagged as special. An argument of type
48/// `std::allocator_arg_t` is used to distinguish constructors that take an
49/// allocator from constructors that don't.
50///
51/// In addition to the `allocator_arg_t` class type, this component (and the
52/// standard) define a constant, `allocator_arg`, of type `allocator_arg_t`.
53/// That constant is used for passing an `allocator_arg_t` argument to a
54/// function or constructor.
55///
56/// ## Usage {#bslmf_allocatorargt-usage}
57///
58///
59/// In this section we show intended use of this component.
60///
61/// ### Example 1: Disambiguate a constructor invocation {#bslmf_allocatorargt-example-1-disambiguate-a-constructor-invocation}
62///
63///
64/// Suppose we want to define a nullable type that can be in the full state
65/// (holding an object) or the null state (not holding an object). When in the
66/// full state, memory is allocated for the held object using a memory
67/// allocator. For simplicity, this memory allocator is not automatically
68/// propagated to the held object.
69///
70/// First, we define a simple allocator class hierarchy with an abstract
71/// `xyzma::Allocator` base class and two derived classes:
72/// `xyzma::NewDeleteAllocator` and `xyzma::TestAllocator`:
73/// @code
74/// #include <cstddef>
75///
76/// namespace xyzma {
77///
78/// class Allocator {
79/// // Abstract allocator base class
80/// public:
81/// virtual ~Allocator() { }
82///
83/// virtual void *allocate(std::size_t nbytes) = 0;
84/// virtual void deallocate(void *ptr) = 0;
85/// };
86///
87/// class NewDeleteAllocator : public Allocator {
88/// // Concrete allocator that uses operators 'new' and 'delete'
89///
90/// public:
91/// static NewDeleteAllocator* singleton();
92/// // Returns a singleton instance of this class
93///
94/// virtual void *allocate(std::size_t nbytes);
95/// virtual void deallocate(void *ptr);
96/// };
97///
98/// NewDeleteAllocator *NewDeleteAllocator::singleton() {
99/// static NewDeleteAllocator s;
100/// return &s;
101/// }
102///
103/// void *NewDeleteAllocator::allocate(std::size_t nbytes) {
104/// return ::operator new(nbytes);
105/// }
106///
107/// void NewDeleteAllocator::deallocate(void *ptr) {
108/// ::operator delete(ptr);
109/// }
110///
111/// class TestAllocator : public Allocator {
112/// // Concrete allocator that keeps track of number of blocks allocated
113/// // and deallocated.
114///
115/// std::size_t d_allocatedBlocks;
116/// std::size_t d_deallocatedBlocks;
117///
118/// public:
119/// TestAllocator() : d_allocatedBlocks(0), d_deallocatedBlocks(0) { }
120///
121/// virtual void *allocate(std::size_t nbytes);
122/// virtual void deallocate(void *ptr);
123///
124/// // ACCESSORS
125/// std::size_t allocatedBlocks() const { return d_allocatedBlocks; }
126/// std::size_t deallocatedBlocks() const { return d_deallocatedBlocks; }
127/// std::size_t outstandingBlocks() const {
128/// return d_allocatedBlocks - d_deallocatedBlocks;
129/// }
130/// };
131///
132/// void *TestAllocator::allocate(std::size_t nbytes) {
133/// ++d_allocatedBlocks;
134/// return ::operator new(nbytes);
135/// }
136///
137/// void TestAllocator::deallocate(void *ptr) {
138/// ++d_deallocatedBlocks;
139/// ::operator delete(ptr);
140/// }
141///
142/// } // close namespace xyzma
143/// @endcode
144/// Next, we define our nullable class template, declaring two constructors: one
145/// that constructs the null object, and one that constructs a non-null object
146/// using the specified constructor argument. For flexibility, the second
147/// constructor is a template that takes any type and can therefore construct
148/// the object without necessarily invoking the copy constructor. (Ideally,
149/// this second constructor would be variadic, but that is not necessary for
150/// this example.):
151/// @code
152/// #include <new>
153///
154/// namespace xyzutl {
155///
156/// template <class TYPE>
157/// class Nullable {
158/// xyzma::Allocator *d_alloc_p;
159/// TYPE *d_object_p;
160///
161/// public:
162/// // CREATORS
163/// Nullable();
164/// // Construct a null object. Note: this is ctor A.
165///
166/// template <class ARG>
167/// Nullable(const ARG& arg);
168/// // Construct a non-null object using the specified 'arg' as the
169/// // constructor argument for the 'TYPE' object. Note: this is ctor
170/// // B.
171/// @endcode
172/// Next, we want to add constructors that supply an allocator for use by the
173/// `Nullable` object. Our first thought is to add two more constructors like
174/// the two above, but with an additional allocator argument at the end:
175/// @code
176/// // Nullable(xyzma::Allocator *alloc);
177/// // ctor C
178///
179/// // template <class ARG>
180/// // Nullable(const ARG& arg, xyzma::Allocator *alloc);
181/// // ctor D
182/// @endcode
183/// However, ctor C is difficult to invoke, because ctor B is almost always a
184/// better match. Nor can we use SFINAE to disqualify ctor B in cases where
185/// ARG is `xyzma::Allocator*` because `xyzma::Allocator*` is a perfectly valid
186/// constructor argument for many `TYPE`s.
187///
188/// We solve this problem by using `allocator_arg_t` to explicitly tag the
189/// constructor that takes an allocator argument:
190/// @code
191/// Nullable(bsl::allocator_arg_t, xyzma::Allocator *alloc);
192/// // Construct a null object with the specified 'alloc' allocator.
193/// // Note: this is ctor E
194/// @endcode
195/// The `allocator_arg_t` argument disambiguates the constructor.
196///
197/// Next, to make things consistent (which is important for generic
198/// programming), we use the `allocator_arg_t` tag in the other allocator-aware
199/// constructor, as well:
200/// @code
201/// template <class ARG>
202/// Nullable(bsl::allocator_arg_t,
203/// xyzma::Allocator *alloc,
204/// const ARG& arg);
205/// // Construct a non-null object using the specified 'arg' as the
206/// // constructor argument for the 'TYPE' object, and the specified
207/// // 'alloc' allocator. Note: this is ctor F.
208/// @endcode
209/// Next, we finish the class interface and implementation:
210/// @code
211/// ~Nullable();
212///
213/// // MANIPULATORS
214/// Nullable& operator=(const Nullable& rhs);
215/// // Copy assign this object from the specified 'rhs'.
216///
217/// Nullable& operator=(const TYPE& rhs);
218/// // Construct a non-null object holding a copy of the specified
219/// // 'rhs' object.
220///
221/// // ACCESSORS
222/// const TYPE& value() const { return *d_object_p; }
223/// // Return the object stored in this Nullable. The behavior is
224/// // undefined if this is null.
225///
226/// bool isNull() const { return ! d_object_p; }
227/// // Returns true if this object is not null.
228///
229/// xyzma::Allocator *allocator() const { return d_alloc_p; }
230/// };
231///
232/// template <class TYPE>
233/// Nullable<TYPE>::Nullable()
234/// : d_alloc_p(xyzma::NewDeleteAllocator::singleton())
235/// , d_object_p(0)
236/// {
237/// }
238///
239/// template <class TYPE>
240/// template <class ARG>
241/// Nullable<TYPE>::Nullable(const ARG& arg)
242/// : d_alloc_p(xyzma::NewDeleteAllocator::singleton())
243/// , d_object_p(static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE))))
244/// {
245/// ::new(d_object_p) TYPE(arg);
246/// }
247///
248/// template <class TYPE>
249/// Nullable<TYPE>::Nullable(bsl::allocator_arg_t, xyzma::Allocator *alloc)
250/// : d_alloc_p(alloc)
251/// , d_object_p(0)
252/// {
253/// }
254///
255/// template <class TYPE>
256/// template <class ARG>
257/// Nullable<TYPE>::Nullable(bsl::allocator_arg_t,
258/// xyzma::Allocator *alloc,
259/// const ARG& arg)
260/// : d_alloc_p(alloc)
261/// , d_object_p(static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE))))
262/// {
263/// ::new(d_object_p) TYPE(arg);
264/// }
265///
266/// template <class TYPE>
267/// Nullable<TYPE>::~Nullable() {
268/// if (d_object_p) {
269/// d_object_p->~TYPE();
270/// d_alloc_p->deallocate(d_object_p);
271/// }
272/// }
273///
274/// template <class TYPE>
275/// Nullable<TYPE>& Nullable<TYPE>::operator=(const Nullable& rhs) {
276/// if (&rhs == this) return *this; // RETURN
277/// if (!isNull() && !rhs.isNull()) {
278/// *d_object_p = *rhs.d_object_p;
279/// }
280/// else if (!isNull() /* && rhs.isNull() */) {
281/// // Make null
282/// d_object_p->~TYPE();
283/// d_alloc_p->deallocate(d_object_p);
284/// d_object_p = 0;
285/// }
286/// else if (/* isNull() && */ !rhs.isNull()) {
287/// // Allocate and copy from 'rhs'
288/// d_object_p = static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE)));
289/// ::new(d_object_p) TYPE(*rhs.d_object_p);
290/// }
291/// // else both are null
292///
293/// return *this;
294/// }
295///
296/// template <class TYPE>
297/// Nullable<TYPE>& Nullable<TYPE>::operator=(const TYPE& rhs) {
298/// if (isNull()) {
299/// d_object_p = static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE)));
300/// ::new(d_object_p) TYPE(*rhs.d_object_p);
301/// }
302/// else {
303/// *d_object_p = rhs;
304/// }
305///
306/// return *this;
307/// }
308///
309/// } // close namespace xyzutl
310/// @endcode
311/// Now, for testing purposes, we define a class that takes an allocator
312/// constructor argument:
313/// @code
314/// class Obj {
315/// xyzma::Allocator *d_alloc_p;
316/// int d_count;
317/// public:
318/// explicit Obj(xyzma::Allocator *alloc = 0)
319/// : d_alloc_p(alloc), d_count(0)
320/// {
321/// }
322///
323/// Obj(int count, xyzma::Allocator *alloc = 0) // IMPLICIT
324/// : d_alloc_p(alloc), d_count(count)
325/// {
326/// }
327///
328/// int count() const { return d_count; }
329/// xyzma::Allocator *allocator() const { return d_alloc_p; }
330/// };
331///
332/// bool operator==(const Obj& a, const Obj& b) {
333/// return a.count() == b.count();
334/// }
335///
336/// bool operator!=(const Obj& a, const Obj& b) {
337/// return a.count() != b.count();
338/// }
339/// @endcode
340/// Finally, we test that our nullable type can be constructed with and without
341/// an allocator pointer and that the allocator pointer can unambiguously be
342/// used for the object's allocator.
343/// @code
344/// int main() {
345///
346/// using xyzutl::Nullable;
347///
348/// xyzma::TestAllocator ta;
349///
350/// Nullable<Obj> no1;
351/// assert( no1.isNull());
352/// assert(xyzma::NewDeleteAllocator::singleton() == no1.allocator());
353///
354/// Nullable<Obj> no2(2);
355/// assert(! no2.isNull());
356/// assert(xyzma::NewDeleteAllocator::singleton() == no2.allocator());
357/// assert(2 == no2.value());
358/// assert(0 == no2.value().allocator());
359///
360/// Nullable<Obj> no3(bsl::allocator_arg, &ta);
361/// assert( no3.isNull());
362/// assert(&ta == no3.allocator());
363/// assert(0 == ta.outstandingBlocks());
364///
365/// Nullable<Obj> no4(bsl::allocator_arg, &ta, 4);
366/// assert(! no4.isNull());
367/// assert(&ta == no4.allocator());
368/// assert(1 == ta.outstandingBlocks());
369/// assert(4 == no4.value());
370/// assert(0 == no4.value().allocator());
371///
372/// // '&ta' used by 'Obj', not by 'Nullable'.
373/// Nullable<Obj> no5(&ta);
374/// assert(! no5.isNull());
375/// assert(xyzma::NewDeleteAllocator::singleton() == no5.allocator());
376/// assert(1 == ta.outstandingBlocks()); // No change
377/// assert(0 == no5.value());
378/// assert(&ta == no5.value().allocator());
379///
380/// // '&ta' used by both 'Nullable' and by 'Obj'
381/// Nullable<Obj> no6(bsl::allocator_arg, &ta, &ta);
382/// assert(! no6.isNull());
383/// assert(&ta == no6.allocator());
384/// assert(2 == ta.outstandingBlocks());
385/// assert(0 == no6.value());
386/// assert(&ta == no6.value().allocator());
387///
388/// return 0;
389/// }
390/// @endcode
391/// @}
392/** @} */
393/** @} */
394
395/** @addtogroup bsl
396 * @{
397 */
398/** @addtogroup bslmf
399 * @{
400 */
401/** @addtogroup bslmf_allocatorargt
402 * @{
403 */
404
405#include <bslscm_version.h>
406
407#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
408
409#include <memory>
410
411namespace bsl {
412
413// In order to use native templates, these types and values must be identical
414// to the standard ones.
415using std::allocator_arg_t;
416using std::allocator_arg;
417
418} // close namespace bsl
419
420#else // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
421
422namespace bsl {
423
424 // ======================
425 // struct allocator_arg_t
426 // ======================
427
428/// Tag type indicating that next argument is an allocator.
429/// TBD: If native library declares `std::allocator_arg_t`, then this
430/// struct should be an replaced by an alias for `std::allocator_arg_t`.
431struct allocator_arg_t {
432
433};
434
435/// Value of type `allocator_arg_t` used as actual argument to function
436/// that takes an allocator argument. Note that `constexpr` would be better
437/// than `const` here, but any compiler that supports `constexpr` would also
438/// provide this class and named value, that should be imported into
439/// namespace `bsl` with using declarations instead.
440static const allocator_arg_t allocator_arg = { };
441
442} // close namespace bsl
443
444#endif // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
445
446#endif // ! defined(INCLUDED_BSLMF_ALLOCATORARGT)
447
448// ----------------------------------------------------------------------------
449// Copyright 2015 Bloomberg Finance L.P.
450//
451// Licensed under the Apache License, Version 2.0 (the "License");
452// you may not use this file except in compliance with the License.
453// You may obtain a copy of the License at
454//
455// http://www.apache.org/licenses/LICENSE-2.0
456//
457// Unless required by applicable law or agreed to in writing, software
458// distributed under the License is distributed on an "AS IS" BASIS,
459// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
460// See the License for the specific language governing permissions and
461// limitations under the License.
462// ----------------------------- END-OF-FILE ----------------------------------
463
464/** @} */
465/** @} */
466/** @} */
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlb_printmethods.h:283