// bslma_autodestructor.h                                             -*-C++-*-
#ifndef INCLUDED_BSLMA_AUTODESTRUCTOR
#define INCLUDED_BSLMA_AUTODESTRUCTOR

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a range proctor to manage an array of objects.
//
//@CLASSES:
//  bslma::AutoDestructor: range proctor to manage an array of objects
//
//@SEE_ALSO: bslma_destructorguard, bslma_destructorproctor
//
//@DESCRIPTION: This component provides a range proctor class template,
// `bslma::AutoDestructor`, to manage an array of (otherwise-unmanaged) objects
// of parameterized `TYPE` supplied at construction.  Unless explicitly
// released, the contiguous managed objects are destroyed automatically when
// the range proctor goes out of scope by calling each (managed) object's
// destructor.  Note that after a proctor object releases its managed objects,
// the same proctor can be reused to conditionally manage another contiguous
// sequence of objects by invoking the `reset` method.
//
///Usage
///-----
// `bslma::AutoDestructor` is normally used to achieve *exception* *safety* in
// an *exception* *neutral* way by automatically destroying
// (otherwise-unmanaged) orphaned objects for an "in-place" array should an
// exception occur.  The following example illustrates the insertion operation
// for a generic array.  Assume that the array initially contains the following
// five elements:
// ```
//    0     1     2     3     4
//  _____ _____ _____ _____ _____ __
// | "A" | "B" | "C" | "D" | "E" |
// `=====^=====^=====^=====^=====^==
// ```
// To insert an element "F" at index position 2, the existing elements at index
// positions 2, 3, and 4 (i.e., "C", "D", and "E") are first shifted right to
// create an empty spot at the specified insert destination (we assume here and
// below that the array has sufficient capacity).  The elements have to be
// shifted one by one by invoking the copy constructor (immediately followed by
// destroying the original elements).  However, should any of the copy
// construction operations throw, all allocated resources from every previous
// copy construction would be leaked.  Using the `bslma::AutoDestructor`
// prevents the leak by invoking the destructor of each of the previously
// copied elements should the proctor go out of scope before the `release`
// method of the proctor is called (such as when the function exits prematurely
// due to an exception):
// ```
//    0     1     2     3     4     5     6
//  _____ _____ _____ _____ _____ _____ _____
// | "A" | "B" | "C" | "D" | "E" |xxxxx|xxxxx|
// `=====^=====^=====^=====^=====^=====^====='
// my_Array                            ^----- bslma::AutoDestructor
// (length = 5)                           (origin = 6, length = 0)
//
//  Note: "xxxxx" denotes uninitialized memory.
// ```
// As each of the elements at index positions beyond the insertion position is
// shifted up by one index position, the proctor (i.e., the proctor's length)
// is *decremented*, thereby *extending* by one the sequence of elements it
// manages *below* its origin (note that when the proctor's length is non-
// positive, the element at the origin is *not* managed).  At the same time,
// the array's length is *decremented* to ensure that each array element is
// always being managed (during an allocation attempt) either by the proctor or
// the array itself, but not both:
// ```
//    0     1     2     3     4     5     6
//  _____ _____ _____ _____ _____ _____ _____
// | "A" | "B" | "C" | "D" |xxxxx| "E" |xxxxx|
// `=====^=====^=====^=====^=====^=====^====='
// my_Array                      ^----------- bslma::AutoDestructor
// (length = 4)                          (origin = 6, length = -1)
//
//  Note: Configuration after shifting up one element.
// ```
// When all elements are shifted, the `bslma::AutoDestructor` will protect the
// entire range of shifted objects:
// ```
//    0     1     2     3     4     5     6
//  _____ _____ _____ _____ _____ _____ _____
// | "A" | "B" |xxxxx| "C" | "D" | "E" |xxxxx|
// `=====^=====^=====^=====^=====^=====^====='
// my_Array          ^----------------------- bslma::AutoDestructor
// (length = 2)                           (origin = 6, length = -3)
//
//   Note: Configuration after shifting up three elements.
// ```
// Next, a new copy of element "F" must be created.  If, during creation, an
// allocation fails and an exception is thrown, the array (now of length 2) is
// in a valid state, while the proctor is responsible for destroying the
// orphaned elements at index positions 3, 4, and 5.  If no exception is
// thrown, the proctor's `release` method is called, releasing its control over
// the temporarily-managed contents:
// ```
// // my_array.h
// // ...
//
// /// This class implements an "in-place" array of objects of
// /// parameterized `TYPE` stored contiguously in memory.
// template <class TYPE>
// class my_Array {
//
//     // DATA
//     TYPE             *d_array_p;      // dynamically allocated array
//     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 `my_Array` object having an initial length and capacity
//     /// of 0.  Optionally specify a `basicAllocator` used to supply
//     /// memory.  If `basicAllocator` is 0, the currently installed
//     /// default allocator is used.
//     my_Array(bslma::Allocator *basicAllocator = 0);
//
//     /// Create a `my_Array` object having an initial length of 0 and
//     /// the specified `initialCapacity`.  Optionally specify a
//     /// `basicAllocator` used to supply memory.  If `basicAllocator` is
//     /// 0, the currently installed default allocator is used.
//     my_Array(int initialCapacity, bslma::Allocator *basicAllocator = 0);
//
//     // ...
//
//     /// Destroy this `my_Array` object and all elements currently
//     /// stored.
//     ~my_Array();
//
//     // MANIPULATORS
//     // ...
//
//     /// Insert (a copy of) the specified `object` of parameterized
//     /// `TYPE` at the specified `dstIndex` position of this array.  All
//     /// values with initial indices at or above `dstIndex` are shifted
//     /// up by one index position.  The behavior is undefined unless
//     /// `0 <= dstIndex` and `dstIndex` is less than the number of items
//     /// in this array.
//     void insert(int dstIndex, const TYPE& object);
//
//     // ...
// };
// ```
// Note that the rest of the `my_Array` interface (above) and implementation
// (below) is omitted as the portion shown is sufficient to demonstrate the use
// of `bslma::AutoDestructor`.
// ```
// // CREATORS
// template <class TYPE>
// inline
// my_Array<TYPE>::my_Array(bslma::Allocator *basicAllocator)
// : d_array_p(0)
// , d_length(0)
// , d_size(0)
// , d_allocator_p(bslma::Default::allocator(basicAllocator))
// {
// }
//
// template <class TYPE>
// my_Array<TYPE>::~my_Array()
// {
//     for (int i = 0; i < d_length; ++i) {
//         d_array_p[i].~TYPE();
//     }
//     d_allocator_p->deallocate(d_array_p);
// }
// ```
// The elided implementation of the following `insert` function (which shows
// code for the case above, i.e., there is sufficient capacity) is sufficient
// to illustrate the use of `bslma::AutoDestructor`:
// ```
// // MANIPULATORS
// template <class TYPE>
// void my_Array<TYPE>::insert(int dstIndex, const TYPE& object)
// {
//     BSLS_ASSERT(0 <= dstIndex);
//     BSLS_ASSERT(dstIndex <= d_length);
//
//     if (d_size == d_length) {  // resize needed
//         // ...
//     }
//
//     const TYPE *tmp = &object;
//
//     if ((d_array_p + dstIndex <= &object)
//      && (&object < d_array_p + d_length)) {  // self-aliasing
//         tmp = &object + 1;
//     }
//
//     //**************************************************************
//     // Note the use of the auto destructor on 'd_array_p' (below). *
//     //**************************************************************
//
//     bslma::AutoDestructor<TYPE> autoDtor(&d_array_p[d_length + 1], 0);
//     int origLen = d_length;
//     for (int i = d_length - 1; i >= dstIndex; --i, --autoDtor,
//                                                               --d_length) {
//         new(&d_array_p[i + 1]) TYPE(d_array_p[i], d_allocator_p);
//                                                        // copy to new index
//         d_array_p[i].~TYPE();                          // destroy original
//     }
//
//     new(&d_array_p[dstIndex]) TYPE(*tmp, d_allocator_p);
//
//     //*****************************************************
//     // Note that the auto destructor is released (below). *
//     //*****************************************************
//
//     autoDtor.release();
//
//     d_length = origLen + 1;
// }
// ```
// Note that the `insert` method assumes the copy constructor of `TYPE` takes
// an allocator as a second argument.  In production code, a constructor proxy
// that checks the traits of `TYPE` (to determine whether `TYPE` indeed uses
// `bslma::Allocator`) should be used (see `bslalg_constructorproxy`).

#include <bslscm_version.h>

#include <bsls_assert.h>
#include <bsls_performancehint.h>

namespace BloombergLP {

namespace bslma {

                        // ====================
                        // class AutoDestructor
                        // ====================

/// This class implements a range proctor that, unless its `release` method
/// has previously been invoked, automatically destroys the contiguous
/// sequence of managed objects upon its own destruction by invoking each
/// object's destructor.  If `release` is invoked (see `release`), the
/// proctor will not destroy any objects upon its own destruction (unless
/// `reset` is invoked to assign the proctor another contiguous sequence of
/// objects for management).  Note that when the length of this object is
/// non-zero, it must refer to a non-null array of objects.
template <class TYPE>
class AutoDestructor {

    // DATA
    TYPE *d_origin_p;  // reference location for the array of managed objects
    int   d_length;    // number of objects to manage (sign encodes direction)

    // NOT IMPLEMENTED
    AutoDestructor(const AutoDestructor&);
    AutoDestructor& operator=(const AutoDestructor&);

  private:
    // PRIVATE MANIPULATORS

    /// Destroy the contiguous sequence of objects managed by this range
    /// proctor (if any) by invoking the destructor of each (managed)
    /// object.  Note that the order in which the managed objects are
    /// destroyed is undefined.  Also note that this method factors out the
    /// destruction logic, which allows the destructor to be declared
    /// `inline` for the common case (the range proctor is released before
    /// being destroyed).
    void destroy();

  public:
    // CREATORS

    /// Create a range proctor to manage an array of objects at the
    /// specified `origin`.  Optionally specify `length` to define its
    /// range, which by default is empty (i.e., `length = 0`).  The sequence
    /// of objects may extend in either direction from `origin`.  A positive
    /// `length` represents the sequence of objects starting at `origin` and
    /// extending "up" to `length` (*not* including the object at the index
    /// position `origin + length`).  A negative `length` represents the
    /// sequence of objects starting at one position below `origin` and
    /// extending "down" to the absolute value of `length` (including the
    /// object at index position `origin + length`).  If `length` is 0, then
    /// this range proctor manages no objects.  The behavior is undefined
    /// unless both `origin` and `length` are zero, or `origin` is non-zero
    /// and all objects (if any) within the managed range (as defined by
    /// `length`) are valid.  Note that when `length` is non-positive, the
    /// object at the origin is *not* managed by this range proctor.  For
    /// example, if `origin` is at the index position 2, a `length` of 2
    /// signifies that the objects at positions 2 and 3 are managed, whereas
    /// a `length` of -2 signifies that the objects at positions 0 and 1 are
    /// managed:
    /// ```
    ///    length = -2                            length = 2
    ///    |<----->|                              |<----->|
    ///     ___ ___ ___ ___ ___            ___ ___ ___ ___ ___
    ///    | 0 | 1 | 2 | 3 | 4 |          | 0 | 1 | 2 | 3 | 4 |
    ///    `===^===^===^===^==='          `===^===^===^===^==='
    ///            ^------------ origin           ^------------ origin
    /// ```
    explicit AutoDestructor(TYPE *origin, int length = 0);

    /// Destroy this range proctor along with the contiguous sequence of
    /// objects it manages (if any) by invoking the destructor of each
    /// (managed) object.  Note that the order in which the managed objects
    /// are destroyed is undefined.
    ~AutoDestructor();

    // MANIPULATORS

    /// Increase by one the (signed) length of the sequence of objects
    /// managed by this range proctor.  The behavior is undefined unless the
    /// origin of the sequence of objects managed by this proctor is
    /// non-zero.  The behavior is undefined unless the origin of this range
    /// proctor is non-zero.  Note that if the length of this proctor is
    /// currently negative, the number of managed objects will decrease by
    /// one, whereas if the length is non-negative, the number of managed
    /// objects will increase by one.
    void operator++();

    /// Decrease by one the (signed) length of the sequence of objects
    /// managed by this range proctor.  The behavior is undefined unless the
    /// origin of the sequence of objects managed by this proctor is
    /// non-zero.  The behavior is undefined unless the origin of this range
    /// proctor is non-zero.  Note that if the length of this proctor is
    /// currently positive, the number of managed objects will decrease by
    /// one, whereas if the length is non-positive, the number of managed
    /// objects will increase by one.
    void operator--();

    /// Release from management the sequence of objects currently managed by
    /// this range proctor by setting the length of the managed sequence to
    /// 0.  All objects currently under management will become unmanaged
    /// (i.e., when the proctor goes out of scope and it was not assigned
    /// another sequence of objects to manage by invoking `reset`, no
    /// objects will be destroyed).  If no objects are currently being
    /// managed, this method has no effect.  Note that the origin is not
    /// affected.
    void release();

    /// Set the specified `origin` of the sequence of objects managed by
    /// this range proctor.  This method does not destroy the
    /// previously-managed objects or affect the length of the sequence
    /// managed.  The behavior is undefined unless `origin` is non-zero.
    /// Note that `reset` can be called without having previously called
    /// `release` provided that `length` is set appropriately before this
    /// proctor could be destroyed (e.g., via a thrown exception).
    void reset(TYPE *origin);

    /// Set the (signed) length of the sequence of objects managed by this
    /// range proctor to the specified `length`.  The behavior is undefined
    /// unless the origin of this range proctor is non-zero.
    void setLength(int length);

    // ACCESSORS

    /// Return the (signed) length of the sequence of objects managed by
    /// this range proctor.
    int length() const;
};

// ============================================================================
//                          INLINE DEFINITIONS
// ============================================================================

                        // --------------------
                        // class AutoDestructor
                        // --------------------

// PRIVATE MANIPULATORS
template <class TYPE>
void AutoDestructor<TYPE>::destroy()
{
    if (0 < d_length) {
        for (TYPE *next_p = d_origin_p; d_length > 0; --d_length) {
            (next_p++)->~TYPE();
        }
    }
    else {
        for (TYPE *next_p = d_origin_p; d_length < 0; ++d_length) {
            (--next_p)->~TYPE();
        }
    }

    BSLS_ASSERT_SAFE(0 == d_length);
}

// CREATORS
template <class TYPE>
inline
AutoDestructor<TYPE>::AutoDestructor(TYPE *origin, int length)
: d_origin_p(origin)
, d_length(length)
{
    BSLS_ASSERT_SAFE(origin || !length);
}

template <class TYPE>
inline
AutoDestructor<TYPE>::~AutoDestructor()
{
    BSLS_ASSERT_SAFE(d_origin_p || !d_length);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_length)) {
        destroy();
    }
}

// MANIPULATORS
template <class TYPE>
inline
void AutoDestructor<TYPE>::operator++()
{
    BSLS_ASSERT_SAFE(d_origin_p);

    ++d_length;
}

template <class TYPE>
inline
void AutoDestructor<TYPE>::operator--()
{
    BSLS_ASSERT_SAFE(d_origin_p);

    --d_length;
}

template <class TYPE>
inline
void AutoDestructor<TYPE>::release()
{
    d_length = 0;
}

template <class TYPE>
inline
void AutoDestructor<TYPE>::reset(TYPE *origin)
{
    BSLS_ASSERT_SAFE(origin);

    d_origin_p = origin;
}

template <class TYPE>
inline
void AutoDestructor<TYPE>::setLength(int length)
{
    BSLS_ASSERT_SAFE(d_origin_p);

    d_length = length;
}

// ACCESSORS
template <class TYPE>
inline
int AutoDestructor<TYPE>::length() const
{
    return d_length;
}

}  // close package namespace

#ifndef BDE_OPENSOURCE_PUBLICATION  // BACKWARD_COMPATIBILITY
// ============================================================================
//                           BACKWARD COMPATIBILITY
// ============================================================================

#ifdef bslma_AutoDestructor
#undef bslma_AutoDestructor
#endif
/// This alias is defined for backward compatibility.
#define bslma_AutoDestructor bslma::AutoDestructor
#endif  // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
