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

Macros

#define bslma_AutoRawDeleter   bslma::AutoRawDeleter
 This alias is defined for backward compatibility.
 

Detailed Description

Outline

Purpose

Provide a range proctor to manage a sequence objects.

Classes

See also
bslma_rawdeleterproctor, bslma_rawdeleterguard

Description

This component provides a range proctor class template, bslma::AutoRawDeleter, to manage a sequence of (otherwise-unmanaged) objects of parameterized TYPE supplied at construction. If not explicitly released, the sequence of managed objects are deleted automatically when the range proctor goes out of scope by iterating over each object, first calling the (managed) object's destructor, and then freeing its memory footprint by invoking the deallocate method of an allocator (or pool) of parameterized ALLOCATOR type also supplied at construction. Note that after a range proctor releases its sequence of managed objects, the same proctor can be reused to conditionally manage another sequence of objects (allocated from the same allocator or pool that was supplied at construction) by invoking the reset method.

"Raw" Warning

Note that this component should be used only if we are sure that the supplied pointer is not of a type that is a secondary base class – i.e., the (managed) object's address is (numerically) the same as when it was originally dispensed by ALLOCATOR.

Requirement

The parameterized ALLOCATOR type of the bslma::AutoRawDeleter class template must provide a (possibly virtual) method:

void deallocate(void *address);

to deallocate memory at the specified address (originally supplied by the ALLOCATOR object).

Usage

The bslma::AutoRawDeleter proctor object can be used to preserve exception neutrality during manipulation of out-of-place arrays of user-defined-type objects. The following illustrates the insertion operation for an "out-of-place" string array. Assume that a string array initially contains the addresses of the following string objects as its elements:

0 1 2 3 4
_____ _____ _____ _____ _____
| o | o | o | o | o |
`==|==^==|==^==|==^==|==^==|=='
_V_ _V_ _V_ _V_ _V_
|"A"| |"B"| |"C"| |"D"| |"E"|
`===' `===' `===' `===' `==='

To insert two string objects with values "F" and "G" at index position 2, the array is first reallocated if it is not big enough, and then the existing elements at index positions 2, 3, and 4 are shifted:

0 1 2 3 4 5 6
_____ _____ _____ _____ _____ _____ _____
| o | o |xxxxx|xxxxx| o | o | o |
`==|==^==|==^=====^=====^==|==^==|==^==|=='
_V_ _V_ _V_ _V_ _V_
|"A"| |"B"| |"C"| |"D"| |"E"|
`===' `===' `===' `===' `==='
Note: "xxxxx" denotes undefined value

Next, two new string objects must be created and initialized with string values "F" and "G", respectively. If, during creation, an allocation fails and an exception is thrown, the array will be left in an invalid state because the addresses contained at index positions 2 and 3 may be duplicates of those at index positions 4 and 5, or, if a resize occurred, invalid altogether. We can restore exception neutrality by setting the array's length to 2 before attempting to create the string objects, but there is still a problem: the string objects "C", "D", and "E" (at index positions 3, 4, and 5) are "orphaned" and will never be deleted – a memory leak. To prevent this potential memory leak, we can additionally create a bslma::AutoRawDeleter object to manage (temporarily) the elements at index positions 4, 5, and 6 prior to creating the new objects:

0 1 2 3 4 5 6
_____ _____ _____ _____ _____ _____ _____
| o | o |xxxxx|xxxxx| o | o | o |
`==|==^==|==^=====^=====^==|==^==|==^==|=='
_V_ _V_ _V_ _V_ _V_
|"A"| |"B"| |"C"| |"D"| |"E"|
`===' `===' `===' `===' `==='
my_StrArray2 ^-----------------bslma::AutoRawDeleter
(length = 2) (length = 3)
Figure: Use of proctor for my_StrArray2::insert
Definition bslma_autorawdeleter.h:381

If an exception occurs, the array (now of length 2) is in a perfectly valid state, while the second proctor is responsible for deleting the orphaned elements at index positions 4, 5, and 6. If no exception is thrown, the elements at index positions 2 and 3 are set to new strings "F" and "G", the length of the first proctor is set to 7 and the second proctor's release method is called, releasing its control over the temporarily managed elements.

The following example illustrates the use of bslma::AutoRawDeleter in conjunction with bslma::DeallocatorProctor to manage temporarily a templatized, "out-of-place" array of parameterized TYPE objects during the array's insertion operation.

First we define a myArray class that stores an array of parameterized TYPE objects:

/// This class is a container that stores an array of objects of
/// parameterized `TYPE`.
template <class TYPE>
class myArray {
// DATA
TYPE **d_array_p; // dynamically allocated array of
// character sequence
int d_length; // logical length of this array
int d_size; // physical capacity of this array
bslma::Allocator *d_allocator_p; // allocator (held, not owned)
public:
// CREATORS
/// Create a `myArray` object. Optionally specify a
/// `basicAllocator` used to supply memory. If `basicAllocator` is
/// 0, the currently installed default allocator is used.
myArray(bslma::Allocator *basicAllocator = 0);
/// Destroy this `myArray` object and all elements currently stored.
~myArray();
// MANIPULATORS
/// Insert into this array at the specified `dstIndex`, the
/// character sequences in the specified `srcArray`. All values
/// with initial indices at or above `dstIndex` are shifted up by
/// `srcArray.length()` index positions. The behavior is undefined
/// unless `0 <= dstIndex` and `dstIndex <= length()`.
void insert(int dstIndex, const myArray& srcArray);
// ...
// ACCESSORS
/// Return the logical length of this array.
int length() const;
// ...
};
Definition bslma_allocator.h:457

Note that a bslma::DeallocatorProctor is used to manage a block of memory allocated before invoking the constructor of TYPE. If the constructor of TYPE throws, the (managed) memory is automatically deallocated by bslma::DeallocatorProctors destructor:

template <class TYPE>
void myArray<TYPE>::insert(int dstIndex, const myArray<TYPE>& srcArray)
{
int srcLength = srcArray.d_length;
int newLength = d_length + srcLength;
int numShifted = d_length - dstIndex;
if (newLength > d_size) { // need to resize
// ...
}
// First shift the elements to the back of the array.
memmove(d_array_p + dstIndex + srcLength,
d_array_p + dstIndex,
numShifted * sizeof *d_array_p);
// Shorten 'd_length' and use 'bslma::AutoDeleter' to proctor tail
// elements.
d_length = dstIndex;
//*************************************************************
// Note the use of auto raw deleter on tail elements (below). *
//*************************************************************
tailDeleter(d_array_p + dstIndex + srcLength,
d_allocator_p,
numShifted);

Now, if any allocation, either allocating memory for new elements or the constructor of the new element throws, the elements that had been moved to the end of the array will be deleted automatically by the bslma::AutoRawDeleter.

// Used to temporarily proctor each new element's memory.
elementDeallocator(0, d_allocator_p);
if (this != &srcArray) { // no self-alias
// Copy the objects one by one.
for (int i = 0; i < srcLength; ++i, ++d_length) {
d_array_p[dstIndex + i] =
(TYPE *) d_allocator_p->allocate(sizeof **d_array_p);
elementDeallocator.reset(d_array_p[dstIndex + i]);
new(d_array_p[dstIndex + i]) TYPE(*srcArray.d_array_p[i],
d_allocator_p);
elementDeallocator.release();
}
}
else { // self-alias
// ...
}
//*********************************************
// Note that the proctor is released (below). *
//*********************************************
tailDeleter.release();
d_length = newLength;
}
Definition bslma_deallocatorproctor.h:312

Note that portions of the implementation are elided as it adds unnecessary complications to the usage example. The shown portion is sufficient to illustrate the use of bslma_autorawdeleter .

The above method copies the source elements (visually) from left to right. Another (functionally equivalent) implementation copies the source elements from right to left, and makes use of the operator--() of the bslma::AutoRawDeleter interface:

template <class TYPE>
void myArray<TYPE>::insert(int dstIndex, const myStrArray<TYPE>& srcArray)
{
int srcLength = srcArray.d_length;
int newLength = d_length + srcLength;
int numShifted = d_length - dstIndex;
if (newLength > d_size) { // need to resize
// ...
}
// First shift the elements to the back of the array.
memmove(d_array_p + dstIndex + srcLength,
d_array_p + dstIndex,
numShifted * sizeof *d_array_p);
// Shorten 'd_length' and use 'bslma::AutoDeallocator' to proctor the
// memory shifted.
d_length = dst_Index;
//********************************************
//* Note the use of auto raw deleter on tail *
//* memory with negative length (below). *
//********************************************
d_allocator_p,
-numShifted);

Since we have decided to copy the source elements from right to left, we set the origin of the bslma::AutoRawDeleter to the end of the array, and decrement the (signed) length on each copy to extend the proctor range by 1.

// Used to temporarily proctor each new element's memory.
elementDeallocator(0, d_allocator_p);
if (this != &srcArray) { // no self-alias
// Copy the character sequences from the 'srcArray'. Note that the
// 'tailDeleter' has to be decremented to cover the newly
// created object.
for (int i = srcLength - 1; i >= 0; --i, --tailDeleter) {
d_array_p[dstIndex + i] =
(TYPE *) d_allocator_p->allocate(sizeof **d_array_p);
elementDeallocator.reset(d_array_p[dstIndex + i]);
new(d_array_p[dstIndex + i]) TYPE(*srcArray.d_array_p[i],
d_allocator_p);
elementDeallocator.release();
}
}
else { // self-alias
// ...
}
//*********************************************
// Note that the proctor is released (below). *
//*********************************************
tailDeleter.release();
d_length = newLength;
}

Note that though the two implementations are functionally equivalent, they are logically different. First of all, the second implementation will be slightly slower because it is accessing memory backwards when compared to the normal forward sequential access. Secondly, in case of an exception, the first implementation will retain all the elements copied prior to the exception, whereas the second implementation will remove them.

Macro Definition Documentation

◆ bslma_AutoRawDeleter

#define bslma_AutoRawDeleter   bslma::AutoRawDeleter