Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslma_autorawdeleter
[Package bslma]

Provide a range proctor to manage a sequence objects. More...

Namespaces

namespace  bslma

Detailed Description

Outline
Purpose:
Provide a range proctor to manage a sequence objects.
Classes:
bslma::AutoRawDeleter range proctor to manage a sequence of objects
See also:
Component bslma_rawdeleterproctor, Component 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
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:
  template <class TYPE>
  class myArray {
      // This class is a container that stores an array of objects of
      // parameterized 'TYPE'.

      // 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
      myArray(bslma::Allocator *basicAllocator = 0);
          // Create a 'myArray' object.  Optionally specify a
          // 'basicAllocator' used to supply memory.  If 'basicAllocator' is
          // 0, the currently installed default allocator is used.

      ~myArray();
          // Destroy this 'myArray' object and all elements currently stored.

      // MANIPULATORS
      void insert(int dstIndex, const myArray& 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
          // 'srcArray.length()' index positions.  The behavior is undefined
          // unless '0 <= dstIndex' and 'dstIndex <= length()'.

      // ...

      // ACCESSORS
      int length() const;
          // Return the logical length of this array.

      // ...
  };
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). *
      //*************************************************************

      bslma::AutoRawDeleter<TYPE, bslma::Allocator>
                                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.
      bslma::DeallocatorProctor<bslma::Allocator>
                                        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;
  }
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).     *
      //********************************************

      bslma::AutoRawDeleter<TYPE, bslma::Allocator> tailDeleter(newLength,
                                                              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.
      bslma::DeallocatorProctor<bslma::Allocator>
                                        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.