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

Macros

#define bslma_AutoDeallocator   bslma::AutoDeallocator
 This alias is defined for backward compatibility.
 

Detailed Description

Outline

Purpose

Provide a range proctor to managed a block of memory.

Classes

See also
bslma_deallocatorguard, bslma_deallocatorproctor

Description

This component provides a range proctor class template, bslma::AutoDeallocator, to manage a sequence of blocks of (otherwise-unmanaged) memory of parameterized TYPE supplied at construction. If not explicitly released, the sequence of managed memory blocks are deallocated automatically when the range proctor goes out of scope by freeing the memory using the parameterized ALLOCATOR (allocator or pool) supplied at construction. Note that after a range proctor releases its managed sequence of memory, the same proctor can be reused to conditionally manage another sequence of memory (allocated from the same allocator or pool that was supplied at construction) by invoking the reset method.

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::AutoDeallocator proctor object can be used to achieve exception safety in an exception neutral way during manipulation of "out-of-place" arrays of raw resources or memory. Since there are no destructor calls, this component is more efficient compared to the bslma::AutoRawDeleter. The following illustrates the insertion operation for an "out-of-place" array of raw character sequences. Assume that an array initially contains 5 character sequences as its elements:

0 1 2 3 4
_____ _____ _____ _____ _____
| o | o | o | o | o |
`==|==^==|==^==|==^==|==^==|=='
| _V___ | __V___ |
| |"Bye"| | |"berg"| |
| `=====' | `======' |
_V_____ _V_____ _V__
|"Hello"| |"Bloom"| |"LP"|
`=======' `=======' `===='

To insert two more character sequences 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___ |
| |"Bye"| | |"berg"| |
| `=====' | `======' |
_V_____ _V_____ _V__
|"Hello"| |"Bloom"| |"LP"|
`=======' `=======' `===='
Note: "xxxxx" denotes undefined value.

Next, two new memory blocks must be allocated to position 2 and 3. If, one of the two allocations 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 character sequences "Bloom", "berg", and "LP" (at index positions 4, 5, and 6) are "orphaned" and will never be deallocated – a memory leak. To prevent this potential memory leak, we can additionally create a bslma::AutoDeallocator object to manage (temporarily) the memory at index positions 4, 5, and 6 prior to allocating the new memory:

0 1 2 3 4 5 6
_____ _____ _____ _____ _____ _____ _____
| o | o |xxxxx|xxxxx| o | o | o |
`==|==^==|==^=====^=====^==|==^==|==^==|=='
| _V___ | __V___ |
| |"Bye"| | |"berg"| |
| `=====' | `======' |
_V_____ _V_____ _V__
|"Hello"| |"Bloom"| |"LP"|
`=======' `=======' `===='
my_StrArray ^---------------bslma::AutoDeallocator
(length = 2) (origin = 4, length = 3)
Note: Configuration after initializing the proctor.
Definition bslma_autodeallocator.h:409

If an exception occurs, the array (now of length 2) is in a perfectly valid state, while the proctor is responsible for deallocating the orphaned memory at index positions 4, 5, and 6. If no exception is thrown, the length is set to 7 and the proctor's release method is called, releasing its control over the (temporarily) managed memory.

The following example illustrates the use of bslma::AutoDeallocator to manage temporarily an "out-of-place" array of character sequences during the array's insertion operation.

First we define a my_StrArray class that stores an array of character sequences.

// my_strarray.h
// ...
/// This class is a container that stores an array of character
/// sequences. Memory will be supplied by the parameterized `ALLOCATOR`
/// type provided at construction (which must remain valid throughout
/// the lifetime of this guard object). Note that memory is managed by
/// a parameterized `ALLOCATOR` type, instead of a `bslma::Allocator`,
/// to enable clients to pass in a pool (such as a sequential pool)
/// optimized for allocations of character sequences.
template <class ALLOCATOR>
class my_StrArray {
// DATA
char **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
ALLOCATOR *d_allocator_p; // allocator or pool (held, not owned)
public:
// CREATORS
/// Create a `my_StrArray` object using the specified
/// `basicAllocator` used to supply memory.
my_StrArray(ALLOCATOR *basicAllocator);
/// Destroy this `my_StrArray` object and all elements currently
/// stored.
~my_StrArray();
// MANIPULATORS
/// Append to this array the string `src`.
void append(const char *src);
/// 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
/// unless `0 <= dstIndex` and `dstIndex <= length()`. Note that
/// this method is functionally the same as `insert2`, but has a
/// different implementation to facilitate the usage example.
void insert(int dstIndex, const my_StrArray& srcArray);
/// 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
/// unless `0 <= dstIndex` and `dstIndex <= length()`. Note that
/// this method is functionally the same as `insert`, but has a
/// different implementation to facilitate the usage example.
void insert2(int dstIndex, const my_StrArray& srcArray);
// ...
// ACCESSORS
/// Return the logical length of this array.
int length() const;
/// Return a pointer to the 'index'th string in the array. Note
/// the pointer is returned by value, it is not a reference to a
/// pointer.
const char *operator[](int index) const;
// ...
};

Next, we define the insert method of my_StrArray:

template <class ALLOCATOR>
void my_StrArray<ALLOCATOR>::insert(int dstIndex,
const my_StrArray<ALLOCATOR>& srcArray)
{
int srcLength = srcArray.d_length;
int newLength = d_length + srcLength;
int numShifted = d_length - dstIndex;
if (newLength > d_size) {
while (d_size < newLength) {
d_size = ! d_size ? 1 : 2 * d_size;
}
char ** newArray =
(char **) d_allocator_p->allocate(d_size * sizeof(char *));
memcpy(newArray, d_array_p, d_length * sizeof(char *));
if (d_array_p) {
d_allocator_p->deallocate(d_array_p);
}
d_array_p = newArray;
}
char **tmpSrc = srcArray.d_array_p;
if (this == &srcArray) {
// self-alias
size_t size = srcLength * sizeof(char *);
tmpSrc = (char **) d_allocator_p->allocate(size);
memcpy(tmpSrc, d_array_p, size);
}
this == &srcArray ? tmpSrc : 0,
d_allocator_p);
// 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 = dstIndex;
//*******************************************************
// Note use of auto deallocator on tail memory (below). *
//*******************************************************
(void **) d_array_p + dstIndex + srcLength,
d_allocator_p,
numShifted);
Definition bslma_deallocatorproctor.h:312
bsl::size_t size(const TYPE &array)
Return the number of elements in the specified array.

Now, if any allocation for the inserted character sequences throws, the memory used for the character sequences that had been moved to the end of array will be deallocated automatically by the bslma::AutoDeallocator.

// Copy the character sequences from the 'srcArray'.
for (int i = 0; i < srcLength; ++i, ++d_length) {
std::size_t size = std::strlen(tmpSrc[i]) + 1;
d_array_p[dstIndex + i] = (char *) d_allocator_p->allocate(size);
memcpy(d_array_p[dstIndex + i], tmpSrc[i], size);
}
//*********************************************
// Note that the proctor is released (below). *
//*********************************************
tailDeallocator.release();
d_length = newLength;
}

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::AutoDeallocator interface:

template <class ALLOCATOR>
void my_StrArray<ALLOCATOR>::insert2(int dstIndex,
const my_StrArray<ALLOCATOR>& srcArray)
{
int srcLength = srcArray.d_length;
int newLength = d_length + srcLength;
int numShifted = d_length - dstIndex;
if (newLength > d_size) {
while (d_size < newLength) {
d_size = ! d_size ? 1 : 2 * d_size;
}
char ** newArray =
(char **) d_allocator_p->allocate(d_size * sizeof(char *));
memcpy(newArray, d_array_p, d_length * sizeof(char *));
if (d_array_p) {
d_allocator_p->deallocate(d_array_p);
}
d_array_p = newArray;
}
char **tmpSrc = srcArray.d_array_p;
if (this == &srcArray) {
// self-alias
size_t size = srcLength * sizeof(char *);
tmpSrc = (char **) d_allocator_p->allocate(size);
memcpy(tmpSrc, d_array_p, size);
}
this == &srcArray ? tmpSrc : 0,
d_allocator_p);
// 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 = dstIndex;
//********************************************
//* Note the use of auto deallocator on tail *
//* memory with negative length (below). *
//********************************************
(void **) d_array_p + d_length + srcLength + numShifted,
d_allocator_p,
-numShifted);

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

// Copy the character sequences from the 'srcArray'. Note that the
// 'tailDeallocator' has to be decremented to cover the newly
// created object.
for (int i = srcLength - 1; i >= 0; --i, --tailDeallocator) {
std::size_t size = std::strlen(tmpSrc[i]) + 1;
d_array_p[dstIndex + i] = (char *)d_allocator_p->allocate(size);
memcpy(d_array_p[dstIndex + i], tmpSrc[i], size);
}
//*********************************************
// Note that the proctor is released (below). *
//*********************************************
tailDeallocator.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_AutoDeallocator

#define bslma_AutoDeallocator   bslma::AutoDeallocator