BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslalg_autoarraymovedestructor.h
Go to the documentation of this file.
1/// @file bslalg_autoarraymovedestructor.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslalg_autoarraymovedestructor.h -*-C++-*-
8#ifndef INCLUDED_BSLALG_AUTOARRAYMOVEDESTRUCTOR
9#define INCLUDED_BSLALG_AUTOARRAYMOVEDESTRUCTOR
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslalg_autoarraymovedestructor bslalg_autoarraymovedestructor
15/// @brief Provide a proctor for destroying arrays.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslalg
19/// @{
20/// @addtogroup bslalg_autoarraymovedestructor
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslalg_autoarraymovedestructor-purpose"> Purpose</a>
25/// * <a href="#bslalg_autoarraymovedestructor-classes"> Classes </a>
26/// * <a href="#bslalg_autoarraymovedestructor-description"> Description </a>
27/// * <a href="#bslalg_autoarraymovedestructor-overview-of-the-operation-of-autoarraymovedestructor"> Overview of the operation of AutoArrayMoveDestructor </a>
28/// * <a href="#bslalg_autoarraymovedestructor-usage"> Usage </a>
29/// * <a href="#bslalg_autoarraymovedestructor-example-1-doubling-the-length-of-an-array"> Example 1: Doubling the Length of an Array </a>
30///
31/// # Purpose {#bslalg_autoarraymovedestructor-purpose}
32/// Provide a proctor for destroying arrays.
33///
34/// # Classes {#bslalg_autoarraymovedestructor-classes}
35///
36/// - bslalg::AutoArrayMoveDestructor: exception-neutrality guard for arrays
37///
38/// @see bslma_autodestructor, bslalg_autoarraydestructor
39///
40/// # Description {#bslalg_autoarraymovedestructor-description}
41/// This component provides a proctor object to manage a contiguous
42/// (in-place) sequence of otherwise-unmanaged instances of a user-defined type.
43/// If not explicitly released, all objects managed by the proctor object are
44/// automatically destroyed by the proctor's destructor or moved back to their
45/// original area, using the @ref bslalg_arraydestructionprimitives and
46/// `std::memmove`. This component is intended to be used only with bit-wise
47/// moveable types, and for a very special purpose as shown in the usage
48/// example.
49///
50/// ## Overview of the operation of AutoArrayMoveDestructor {#bslalg_autoarraymovedestructor-overview-of-the-operation-of-autoarraymovedestructor}
51///
52///
53/// Suppose we want to double the length of an array by prepending copies a
54/// `value` at the start of the array. Note that we assume there is ample
55/// uninitialized memory after the end of the initial array for these new
56/// values to be inserted.
57///
58/// Legend:
59/// @code
60/// 'ABCDE' -- initial array elements.
61/// 'v' -- copy of specified 'value' being inserted.
62/// '.' -- (period) uninitialized memory.
63/// '^(,)' -- area guarded by 'AutoArrayMoveDestructor', where:
64/// '^' -- position of 'guard.destination()'
65/// '(' -- position of 'guard.begin()'
66/// ',' -- (comma) position of 'guard.middle()'
67/// ')' -- position of 'guard.end()'
68/// @endcode
69/// The copy constructor for the type being inserted may throw, so we need to
70/// have a guard object that allows us to make some guarantee about the state of
71/// the array after the guard is destroyed. What we want to guarantee is that
72/// there are as many valid objects at the start of the array as before with no
73/// other valid objects in existence.
74///
75/// The following steps show a successful operation prepending copies of the
76/// value `v`:
77/// @code
78/// 1: 'ABCDE.....' -- initial memory.
79/// 2: '.....ABCDE' -- memory after first 'std::memcpy'.
80/// 3: '^.....(,ABCDE)' -- memory immediately after 'guard' is set
81/// 4: 'vv^...(AB,CDE)' -- memory after 2 copies of 'value' have been
82/// created, and 'guard.advance()' has been called
83/// twice.
84/// 5: 'vvvvv^(ABCDE,)' -- memory after insertion is completed
85/// 6: 'vvvvvABCDE' -- memory after guard was destroyed (at which point
86/// 'guard.middle() == guard.end()' so destructor did
87/// nothing.
88/// @endcode
89/// Now suppose we threw after step 4, destroying `guard`.
90/// @code
91/// 4: 'vv^...(AB,CDE)' -- same as step '4' above, before destructor starts
92/// 5b: 'vv^CDE(AB,...)' -- memory after 'guard's destructor moves 'CDE' back
93/// to their position before we began
94/// 6b: 'vv^CDE(..,...)' -- memory after 'guard's destructor destroys 'A' and
95/// 'B'
96/// 7b: 'vvCDE.....' -- memory after 'guard's destructor completes
97/// @endcode
98/// We now have 5 valid elements in the beginning of the range, as it was when
99/// we started, making the situation predictable for our next destructor.
100///
101/// This was a very simple case, but using this guard in conjunction with
102/// `bslalg::AutoArrayDestructor`, we can implement the more general case of
103/// inserting arbitrary numbers of elements at the beginning of an array.
104///
105/// ## Usage {#bslalg_autoarraymovedestructor-usage}
106///
107///
108/// In this section we show intended use of this component.
109///
110/// ### Example 1: Doubling the Length of an Array {#bslalg_autoarraymovedestructor-example-1-doubling-the-length-of-an-array}
111///
112///
113/// First, we create the class `TestType`, which is bitwise-movable and
114/// allocates memory upon construction:
115/// @code
116/// // ==============
117/// // class TestType
118/// // ==============
119///
120/// /// This test type contains a `char` in some allocated storage. It
121/// /// counts the number of default and copy constructions, assignments,
122/// /// and destructions. It has no traits other than using a `bslma`
123/// /// allocator. It could have the bit-wise moveable traits but we defer
124/// /// that trait to the `MoveableTestType`.
125/// class TestType {
126///
127/// char *d_data_p;
128/// bslma::Allocator *d_allocator_p;
129///
130/// public:
131/// // CREATORS
132/// explicit TestType(bslma::Allocator *basicAllocator = 0)
133/// : d_data_p(0)
134/// , d_allocator_p(bslma::Default::allocator(basicAllocator))
135/// {
136/// ++numDefaultCtorCalls;
137/// d_data_p = (char *)d_allocator_p->allocate(sizeof(char));
138/// *d_data_p = '?';
139/// }
140///
141/// explicit TestType(char c, bslma::Allocator *basicAllocator = 0)
142/// : d_data_p(0)
143/// , d_allocator_p(bslma::Default::allocator(basicAllocator))
144/// {
145/// ++numCharCtorCalls;
146/// d_data_p = (char *)d_allocator_p->allocate(sizeof(char));
147/// *d_data_p = c;
148/// }
149///
150/// TestType(const TestType& original,
151/// bslma::Allocator *basicAllocator = 0)
152/// : d_data_p(0)
153/// , d_allocator_p(bslma::Default::allocator(basicAllocator))
154/// {
155/// ++numCopyCtorCalls;
156/// if (&original != this) {
157/// d_data_p = (char *)d_allocator_p->allocate(sizeof(char));
158/// *d_data_p = *original.d_data_p;
159/// }
160/// }
161///
162/// ~TestType()
163/// {
164/// ++numDestructorCalls;
165/// *d_data_p = '_';
166/// d_allocator_p->deallocate(d_data_p);
167/// d_data_p = 0;
168/// }
169///
170/// // MANIPULATORS
171/// TestType& operator=(const TestType& rhs)
172/// {
173/// ++numAssignmentCalls;
174/// if (&rhs != this) {
175/// char *newData = (char *)d_allocator_p->allocate(sizeof(char));
176/// *d_data_p = '_';
177/// d_allocator_p->deallocate(d_data_p);
178/// d_data_p = newData;
179/// *d_data_p = *rhs.d_data_p;
180/// }
181/// return *this;
182/// }
183///
184/// void setDatum(char c) { *d_data_p = c; }
185///
186/// // ACCESSORS
187/// char datum() const { return *d_data_p; }
188///
189/// void print() const
190/// {
191/// if (d_data_p) {
192/// assert(isalpha(*d_data_p));
193/// printf("%c (int: %d)\n", *d_data_p, (int)*d_data_p);
194/// } else {
195/// printf("VOID\n");
196/// }
197/// }
198/// };
199///
200/// // FREE OPERATORS
201/// bool operator==(const TestType& lhs, const TestType& rhs)
202/// {
203/// return lhs.datum() == rhs.datum();
204/// }
205///
206/// // TRAITS
207/// namespace BloombergLP {
208///
209/// namespace bslma {
210/// template <> struct UsesBslmaAllocator<TestType> : bsl::true_type {};
211/// } // close namespace bslma
212///
213/// namespace bslmf {
214/// template <> struct IsBitwiseMoveable<TestType> : bsl::true_type {};
215/// } // close namespace bslmf
216///
217/// } // close enterprise namespace
218/// @endcode
219/// Then, we define the function `insertItems` which uses
220/// `AutoArrayMoveDestructor` to ensure that if an exception is thrown (e.g.,
221/// when allocating memory), the array will be left in a state where it has the
222/// same number of elements, in the same location, as when the function begin
223/// (though not necessarily the same value).
224/// @code
225/// /// The memory in the specified range `[ start, divider )` contains
226/// /// valid elements, and the range of valid elements is to be doubled by
227/// /// inserting `divider - start` copies of the specified `value` at
228/// /// `start`, shifting the existing valid values back in memory. Assume
229/// /// that following the pointer `divider` is sufficient uninitialized
230/// /// memory, and that the type `TestType` is bitwise-movable
231/// /// (`AutoArrayMoveDestructor` will only work bitwise-movable types).
232/// void insertItems(TestType *start,
233/// TestType *divider,
234/// const TestType value,
235/// bslma::Allocator *allocator)
236/// {
237/// TestType *finish = divider + (divider - start);
238///
239/// BSLMF_ASSERT(bslmf::IsBitwiseMoveable< TestType>::value);
240/// BSLMF_ASSERT(bslma::UsesBslmaAllocator<TestType>::value);
241///
242/// // The range '[ start, divider )' contains valid elements. The range
243/// // '[ divider, finish )' is of equal length and contains uninitialized
244/// // memory. We want to insert 'divider - start' copies of the specified
245/// // 'value' at the front half of the range '[ start, finish )', moving
246/// // the existing elements back to make room for them. Note that the
247/// // copy constructor of 'TestType' allocates memory and may throw, so we
248/// // have to leave the array in a somewhat predictable state if we do
249/// // throw. What the bslalg::AutoArrayMoveDestructor will do is
250/// // guarantee that, if it is destroyed before the insertion is complete,
251/// // the range '[ start, divider )' will contain valid elements, and that
252/// // no other valid elements will exist.
253/// //
254/// // Note that the existing elements, which are bitwise-moveable, may be
255/// // *moved* about the container without the possibility of throwing an
256/// // exception, but the newly inserted elements must be copy-constructed
257/// // (requiring memory allocation).
258/// //
259/// // First, move the valid elements from '[ start, divider )' to
260/// // '[ divider, finish )'. This can be done without risk of a throw
261/// // occurring.
262///
263/// std::memcpy((void *)divider,
264/// start,
265/// (divider - start) * sizeof(TestType));
266///
267/// typedef bslalg::AutoArrayMoveDestructor<TestType> Obj;
268/// Obj guard(start, divider, divider, finish, allocator);
269///
270/// while (guard.middle() < guard.end()) {
271/// // Call the copy constructor, which may throw.
272///
273/// new (guard.destination()) TestType(value, allocator);
274///
275/// // 'guard.advance()' increments 'guard.destination()' and
276/// // 'guard.middle()' by one.
277///
278/// guard.advance();
279/// }
280/// }
281/// @endcode
282/// Next, within the `main` function of our task, we create our `value` object,
283/// whose value will be `v`, to be inserted into the front of our range.
284/// @code
285/// TestType value('v');
286/// @endcode
287/// Then, we create a test allocator, and use it to allocate memory for an array
288/// of `TestType` objects:
289/// @code
290/// bslma::TestAllocator ta;
291///
292/// TestType *array = (TestType *) ta.allocate(10 * sizeof(TestType));
293/// @endcode
294/// Next, we construct the first 5 elements of the array to have the values
295/// `ABCDE`.
296/// @code
297/// TestType *p = array;
298/// new (p++) TestType('A', &ta);
299/// new (p++) TestType('B', &ta);
300/// new (p++) TestType('C', &ta);
301/// new (p++) TestType('D', &ta);
302/// new (p++) TestType('E', &ta);
303/// @endcode
304/// Then, we record the number of outstanding blocks in the allocator:
305/// @code
306/// const bsls::Types::Int64 N = ta.numBlocksInUse();
307/// @endcode
308/// Next, we enter an `exception test` block, which will repeatedly enter a
309/// block of code, catching exceptions throw by the test allocator `ta` on each
310/// iteration:
311/// @code
312/// BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(ta)
313/// @endcode
314/// Then, we observe that even if we've just caught an exception and re-entered
315/// the block, the amount of memory outstanding is unchanged from before we
316/// entered the block.
317/// @code
318/// assert(ta.numBlocksInUse() == N);
319/// @endcode
320/// Note that when we threw, some of the values of the 5 elements of the array
321/// may have been changed to `v`, otherwise they will be unchanged.
322///
323/// Next, we re-initialize those elements that have been overwritten in the last
324/// pass with `value` to their values before we entered the block:
325/// @code
326/// if ('v' == array[0].datum()) array[0].setDatum('A');
327/// if ('v' == array[1].datum()) array[1].setDatum('B');
328/// if ('v' == array[2].datum()) array[2].setDatum('C');
329/// if ('v' == array[3].datum()) array[3].setDatum('D');
330/// if ('v' == array[4].datum()) array[4].setDatum('E');
331/// @endcode
332/// Then, we call `insertItems`, which may throw:
333/// @code
334/// insertItems(array, p, value, &ta);
335/// @endcode
336/// Next, we exit the except testing block.
337/// @code
338/// BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
339/// @endcode
340/// Now, we verify that:
341/// 1. we have allocated exactly 5 more blocks of memory, since each `TestType`
342/// object allocates one block and `insertItems` created 5 more `TestType`
343/// objects.
344/// 2. the values of the elements of the array are as expected.
345/// @code
346/// assert(ta.numBlocksInUse() == N + 5);
347///
348/// assert('v' == array[0].datum());
349/// assert('v' == array[1].datum());
350/// assert('v' == array[2].datum());
351/// assert('v' == array[3].datum());
352/// assert('v' == array[4].datum());
353/// assert('A' == array[5].datum());
354/// assert('B' == array[6].datum());
355/// assert('C' == array[7].datum());
356/// assert('D' == array[8].datum());
357/// assert('E' == array[9].datum());
358/// @endcode
359/// Finally, we destroy our array and check the allocator to verify no memory
360/// was leaked:
361/// @code
362/// for (int i = 0; i < 10; ++i) {
363/// array[i].~TestType();
364/// }
365/// ta.deallocate(array);
366///
367/// assert(0 == ta.numBytesInUse());
368/// @endcode
369/// @}
370/** @} */
371/** @} */
372
373/** @addtogroup bsl
374 * @{
375 */
376/** @addtogroup bslalg
377 * @{
378 */
379/** @addtogroup bslalg_autoarraymovedestructor
380 * @{
381 */
382
383#include <bslscm_version.h>
384
386
387#include <bslma_bslallocator.h>
388
389#include <bslmf_assert.h>
391
392#include <bsls_assert.h>
393
394#include <cstddef> // 'std::size_t'
395#include <cstring> // 'memset', 'memcpy', 'memmove'
396
397
398
399namespace bslalg {
400
401 // =============================
402 // class AutoArrayMoveDestructor
403 // =============================
404
405/// This `class` provides a specialized proctor object that, upon
406/// destruction and unless the `release` method has been called, bit-wise
407/// moves the elements in a segment of an array of parameterized
408/// `OBJECT_TYPE` back to some destination, and destroys some other elements
409/// in an adjacent segment of the same array. The elements destroyed are
410/// delimited by the range `[ begin(), middle() )` and those moved to
411/// `destination()` and in the range `[ middle(), end() )`. Note that, once
412/// constructed, `begin()` and `end()` remain fixed. As the guard advances,
413/// `middle()` and `destination()` move, reflecting the successful transfer
414/// of data between the moving range and the destination.
415///
416/// See @ref bslalg_autoarraymovedestructor
417template <class OBJECT_TYPE, class ALLOCATOR = bsl::allocator<OBJECT_TYPE> >
419
420 // DATA
421 OBJECT_TYPE *d_dst_p; // destination of the bit-wise move
422
423 OBJECT_TYPE *d_begin_p; // address of first element in guarded range
424
425 OBJECT_TYPE *d_middle_p; // address of first moved element in guarded range
426 // that is also first address beyond last element
427 // destroyed in same guarded range
428
429 OBJECT_TYPE *d_end_p; // first address beyond last (moved) element in
430 // guarded range
431 ALLOCATOR d_allocator; // allocator
432
433 // CLASS INVARIANT
435
436 private:
437 // NOT IMPLEMENTED
440
441 public:
442 // CREATORS
443
444 /// TBD: document the `allocator` parameter.
445 /// Create a proctor for the sequence of elements of the parameterized
446 /// `OBJECT_TYPE` in the specified range `[ begin, end )` which, upon
447 /// destruction, moves the range `[ begin, middle )` to the specified
448 /// `destination` and destroys the `[ middle, end )` range. The
449 /// behavior is undefined unless `begin`, `middle`, and `end` refer to
450 /// a contiguous sequence of initialized `OBJECT_TYPE` objects, where
451 /// `begin <= middle <= end`, and `destination` refers to a contiguous
452 /// sequence of (uninitialized) memory of sufficient size to hold
453 /// `end - begin` `OBJECT_TYPE` objects (which must not overlap
454 /// `[begin, end)`).
456 OBJECT_TYPE *begin,
457 OBJECT_TYPE *middle,
458 OBJECT_TYPE *end,
459 ALLOCATOR allocator = ALLOCATOR());
460
461 /// Bit-wise move the range `[ middle(), end() )` to the `destination()`
462 /// address and destroy `[ begin(), middle() )`.
464
465 // MANIPULATORS
466
467 /// Increment both middle and destination pointers by one position. The
468 /// behavior is undefined if this operation result in `destination()`
469 /// entering the `[ begin(), end() )` range.
470 void advance();
471
472 // ACCESSORS
473
474 /// Return the address at the beginning of the guarded range.
475 OBJECT_TYPE *begin() const;
476
477 /// Return the destination address, to which the second portion of the
478 /// guarded range, delimited by `[ middle(), end() )`, will be moved
479 /// upon destruction, or 0 if this guard has been released.
480 OBJECT_TYPE *destination() const;
481
482 /// Return the address at the end of the guarded range.
483 OBJECT_TYPE *end() const;
484
485 /// Return the address at the middle of the guarded range.
486 OBJECT_TYPE *middle() const;
487};
488
489// ============================================================================
490// INLINE FUNCTION DEFINITIONS
491// ============================================================================
492
493 // -----------------------------
494 // class AutoArrayMoveDestructor
495 // -----------------------------
496
497// CREATORS
498template <class OBJECT_TYPE, class ALLOCATOR>
499inline
501 OBJECT_TYPE *destination,
502 OBJECT_TYPE *begin,
503 OBJECT_TYPE *middle,
504 OBJECT_TYPE *end,
505 ALLOCATOR allocator)
506: d_dst_p(destination)
507, d_begin_p(begin)
508, d_middle_p(middle)
509, d_end_p(end)
510, d_allocator(allocator)
511{
512 BSLS_ASSERT_SAFE(!begin == !middle); // neither or both are null
513 BSLS_ASSERT_SAFE(!middle == !end); // neither or both are null
517
518}
519
520template <class OBJECT_TYPE, class ALLOCATOR>
522{
523 BSLS_ASSERT_SAFE(!d_begin_p == !d_middle_p); // neither or both are null
524 BSLS_ASSERT_SAFE(!d_middle_p == !d_end_p); // neither or both are null
525 BSLS_ASSERT_SAFE(d_dst_p || d_begin_p == d_middle_p);
526 BSLS_ASSERT_SAFE(d_begin_p <= d_middle_p);
527 BSLS_ASSERT_SAFE(d_middle_p <= d_end_p);
528 BSLS_ASSERT_SAFE(d_dst_p < d_begin_p
529 || d_end_p <= d_dst_p
530 || d_middle_p == d_end_p);
531
532 if (d_middle_p != d_end_p) {
533 std::size_t numBytes = (char *)d_end_p - (char *)d_middle_p;
534 std::memcpy((void *)d_dst_p, d_middle_p, numBytes);
536 d_middle_p,
537 d_allocator);
538 }
539}
540
541// MANIPULATORS
542template <class OBJECT_TYPE, class ALLOCATOR>
543inline
545{
546 BSLS_ASSERT_SAFE(d_middle_p < d_end_p);
547
548 ++d_middle_p;
549 ++d_dst_p;
550
551 BSLS_ASSERT_SAFE(d_dst_p != d_begin_p || d_middle_p == d_end_p);
552}
553
554// ACCESSORS
555template <class OBJECT_TYPE, class ALLOCATOR>
556inline
558{
559 return d_begin_p;
560}
561
562template <class OBJECT_TYPE, class ALLOCATOR>
563inline
564OBJECT_TYPE *
569
570template <class OBJECT_TYPE, class ALLOCATOR>
571inline
573{
574 return d_end_p;
575}
576
577template <class OBJECT_TYPE, class ALLOCATOR>
578inline
580{
581 return d_middle_p;
582}
583
584} // close package namespace
585
586#ifndef BDE_OPENSOURCE_PUBLICATION // BACKWARD_COMPATIBILITY
587// ============================================================================
588// BACKWARD COMPATIBILITY
589// ============================================================================
590
591#ifdef bslalg_AutoArrayMoveDestructor
592#undef bslalg_AutoArrayMoveDestructor
593#endif
594/// This alias is defined for backward compatibility.
595#define bslalg_AutoArrayMoveDestructor bslalg::AutoArrayMoveDestructor
596#endif // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY
597
598
599
600#endif
601
602// ----------------------------------------------------------------------------
603// Copyright 2013 Bloomberg Finance L.P.
604//
605// Licensed under the Apache License, Version 2.0 (the "License");
606// you may not use this file except in compliance with the License.
607// You may obtain a copy of the License at
608//
609// http://www.apache.org/licenses/LICENSE-2.0
610//
611// Unless required by applicable law or agreed to in writing, software
612// distributed under the License is distributed on an "AS IS" BASIS,
613// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
614// See the License for the specific language governing permissions and
615// limitations under the License.
616// ----------------------------- END-OF-FILE ----------------------------------
617
618/** @} */
619/** @} */
620/** @} */
Definition bslalg_autoarraymovedestructor.h:418
OBJECT_TYPE * middle() const
Return the address at the middle of the guarded range.
Definition bslalg_autoarraymovedestructor.h:579
OBJECT_TYPE * destination() const
Definition bslalg_autoarraymovedestructor.h:565
void advance()
Definition bslalg_autoarraymovedestructor.h:544
OBJECT_TYPE * begin() const
Return the address at the beginning of the guarded range.
Definition bslalg_autoarraymovedestructor.h:557
~AutoArrayMoveDestructor()
Definition bslalg_autoarraymovedestructor.h:521
OBJECT_TYPE * end() const
Return the address at the end of the guarded range.
Definition bslalg_autoarraymovedestructor.h:572
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:229
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlc_flathashmap.h:1805
static void destroy(TARGET_TYPE *begin, TARGET_TYPE *end, ALLOCATOR allocator, bsl::true_type)
Definition bslalg_arraydestructionprimitives.h:232
Definition bslmf_isbitwisemoveable.h:718