// bslalg_autoarraydestructor.h                                       -*-C++-*-
#ifndef INCLUDED_BSLALG_AUTOARRAYDESTRUCTOR
#define INCLUDED_BSLALG_AUTOARRAYDESTRUCTOR

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

//@PURPOSE: Provide a proctor for destroying arrays.
//
//@CLASSES:
//  bslalg::AutoArrayDestructor: exception-neutrality proctor for arrays
//
//@SEE_ALSO: bslma_autodestructor
//
//@DESCRIPTION: This component provides a proctor object to manage a contiguous
// (in-place) sequence of otherwise-unmanaged instances of a user-defined type.
// If not explicitly released, all objects managed by the proctor object are
// automatically destroyed by the proctor's destructor, using the
// `bslalg_arraydestructionprimitives`.
//
// In most instances, `bslma::AutoDestructor` can also be used, but this
// component is more useful in cases where it is simpler to think in terms of
// two pointers at the ends of the array being managed, rather than an origin
// and offset.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Example 1: Managing an Array Under Construction
///- - - - - - - - - - - - - - - - - - - - - - - -
// In most instances, the use of a `bslalg::AutoArrayDestructor` could be
// handled by a `bslma::AutoDeallocator`, but sometimes it is conceptually
// clearer to frame the problem in terms of a pair of pointers rather than a
// pointer and an offset.
//
// Suppose we have a class, `UsageType` that allocates a block of memory upon
// construction, and whose constructor takes a `char`.  Suppose we want to
// create an array of elements of such objects in an exception-safe manner.
//
// First, we create the type `UsageType`:
// ```
//                              // ===============
//                              // class UsageType
//                              // ===============
//
// /// This test type contains a 'char' in some allocated storage.  It has
// /// no traits other than using a 'bslma' allocator.
// class UsageType {
//
//     char             *d_data_p;         // managed single char
//     bslma::Allocator *d_allocator_p;    // allocator (held, not owned)
//
//   public:
//     // CREATORS
//     explicit UsageType(char c, bslma::Allocator *basicAllocator = 0)
//     : d_data_p(0)
//     , d_allocator_p(bslma::Default::allocator(basicAllocator))
//     {
//         d_data_p  = (char *)d_allocator_p->allocate(sizeof(char));
//         *d_data_p = c;
//     }
//
//     ~UsageType()
//     {
//         *d_data_p = '_';
//         d_allocator_p->deallocate(d_data_p);
//         d_data_p = 0;
//     }
//
//     // ACCESSORS
//     char datum() const
//     {
//         return *d_data_p;
//     }
// };
//
// namespace BloombergLP {
// namespace bslma {
//
// template <>
// struct UsesBslmaAllocator<UsageType> : bsl::true_type {};
//
// }  // close package namespace
// }  // close enterprise namespace
// ```
// Then, in `main`, we create a `TestAllocator` to supply memory (and to verify
// that no memory is leaked):
// ```
// bslma::TestAllocator ta;
// ```
// Next, we create the pointer for our array:
// ```
// UsageType *array;
// ```
// Then, we declare a string of characters we will use to initialize the
// `UsageType` objects in our array.
// ```
// const char   *DATA = "Hello";
// const size_t  DATA_LEN = std::strlen(DATA);
// ```
// Next, we verify that even right after exceptions have been thrown and
// caught, no memory is outstanding:
// ```
// assert(0 == ta.numBlocksInUse());
// ```
// Then, we allocate our array and create a guard to free it if a subsequent
// allocation throws an exception:
// ```
// array = (UsageType *) ta.allocate(DATA_LEN * sizeof(UsageType));
// bslma::DeallocatorProctor<bslma::Allocator> arrayProctor(array, &ta);
// ```
// Next, we establish an `AutoArrayDestructor` on `array` to destroy any valid
// elements in `array` if an exception is thrown:
// ```
// bslalg::AutoArrayDestructor<UsageType,> arrayElementProctor(array, array);
// ```
// Notice that we pass `arrayElementProctor` pointers to the beginning and end
// of the range to be guarded (we start with an empty range since no elements
// have been constructed yet).
//
// Then, we iterate through the valid chars in `DATA` and use them to construct
// the elements of the array:
// ```
// UsageType *resultElement = array;
// for (const char *nextChar = DATA; *nextChar; ++nextChar) {
// ```
// Next, construct the next element of `array`:
// ```
//     new (resultElement++) UsageType(*nextChar, &ta);
// ```
// Now, move the end of `arrayElementProctor` to cover the most recently
// constructed element:
// ```
//     arrayElementProctor.moveEnd(1);
// }
// ```
// At this point, we have successfully created our array.
//
// Then, release the guards so they won't destroy our work when they go out of
// scope:
// ```
// arrayProctor.release();
// arrayElementProctor.release();
// ```
// Next, exit the exception testing block:
//
// Then, verify that the array we have created is as expected:
// ```
// assert('H' == array[0].datum());
// assert('e' == array[1].datum());
// assert('l' == array[2].datum());
// assert('l' == array[3].datum());
// assert('o' == array[4].datum());
// ```
// Finally, destroy & free our work and verify that no memory is leaked:
// ```
// for (size_t i = 0; i < DATA_LEN; ++i) {
//     array[i].~UsageType();
// }
// ta.deallocate(array);
//
// assert(0 == ta.numBlocksInUse());
// ```

#include <bslscm_version.h>

#include <bslalg_arraydestructionprimitives.h>

#include <bslma_bslallocator.h>

#include <bsls_assert.h>

#include <cstddef>  // size_t

namespace BloombergLP {

namespace bslalg {

                        // =========================
                        // class AutoArrayDestructor
                        // =========================

/// This `class` provides a specialized proctor object that, upon
/// destruction and unless the `release` method has been called, destroys
/// the elements in a segment of an array of parameterized type
/// `OBJECT_TYPE`.  The elements destroyed are delimited by the "guarded"
/// range `[ begin(), end() )`.
template <class OBJECT_TYPE, class ALLOCATOR = bsl::allocator<OBJECT_TYPE> >
class AutoArrayDestructor {

    // DATA
    OBJECT_TYPE *d_begin_p;    // address of first element in guarded range
    OBJECT_TYPE *d_end_p;      // first address beyond last element in guarded
                               // range
    ALLOCATOR    d_allocator;  // allocator

  private:
    // NOT IMPLEMENTED
    AutoArrayDestructor(const AutoArrayDestructor&);
    AutoArrayDestructor& operator=(const AutoArrayDestructor&);

  public:
    // TYPES
    typedef std::ptrdiff_t difference_type;

    // CREATORS

    /// Create an array exception guard object for the sequence of elements
    /// of the parameterized `OBJECT_TYPE` delimited by the range specified
    /// by `[ begin, end )`.  The behavior is undefined unless
    /// `begin <= end` and each element in the range `[ begin, end )` has
    /// been initialized.
    AutoArrayDestructor(OBJECT_TYPE *begin, OBJECT_TYPE *end,
                        ALLOCATOR allocator = ALLOCATOR());

    /// Call the destructor on each of the elements of the parameterized
    /// `OBJECT_TYPE` delimited by the range `[ begin(), end() )` and
    /// destroy this array exception guard.
    ~AutoArrayDestructor();

    // MANIPULATORS

    /// Move the begin pointer by the specified `offset`, and return the new
    /// begin pointer.
    OBJECT_TYPE *moveBegin(difference_type offset = -1);

    /// Move the end pointer by the specified `offset`, and return the new
    /// end pointer.
    OBJECT_TYPE *moveEnd(difference_type offset = 1);

    /// Set the range of elements guarded by this object to be empty.  Note
    /// that `begin() == end()` following this operation, but the specific
    /// value is unspecified.
    void release();
};

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

                      // -------------------------
                      // class AutoArrayDestructor
                      // -------------------------

// CREATORS
template <class OBJECT_TYPE, class ALLOCATOR>
inline
AutoArrayDestructor<OBJECT_TYPE, ALLOCATOR>::AutoArrayDestructor(
                                                        OBJECT_TYPE *begin,
                                                        OBJECT_TYPE *end,
                                                        ALLOCATOR    allocator)
: d_begin_p(begin)
, d_end_p(end)
, d_allocator(allocator)
{
    BSLS_ASSERT_SAFE(!begin == !end);
    BSLS_ASSERT_SAFE(begin <= end);
}

template <class OBJECT_TYPE, class ALLOCATOR>
inline
AutoArrayDestructor<OBJECT_TYPE, ALLOCATOR>::~AutoArrayDestructor()
{
    BSLS_ASSERT_SAFE(!d_begin_p == !d_end_p);
    BSLS_ASSERT_SAFE(d_begin_p <= d_end_p);

    ArrayDestructionPrimitives::destroy(d_begin_p, d_end_p, d_allocator);
}

// MANIPULATORS
template <class OBJECT_TYPE, class ALLOCATOR>
inline
OBJECT_TYPE *AutoArrayDestructor<OBJECT_TYPE, ALLOCATOR>::moveBegin(
                                                        difference_type offset)
{
    BSLS_ASSERT_SAFE(d_begin_p || 0 == offset);
    BSLS_ASSERT_SAFE(d_end_p - d_begin_p >= offset);

    d_begin_p += offset;
    return d_begin_p;
}

template <class OBJECT_TYPE, class ALLOCATOR>
inline
OBJECT_TYPE *
AutoArrayDestructor<OBJECT_TYPE, ALLOCATOR>::moveEnd(difference_type offset)
{
    BSLS_ASSERT_SAFE(d_end_p || 0 == offset);
    BSLS_ASSERT_SAFE(d_end_p - d_begin_p >= -offset);

    d_end_p += offset;
    return d_end_p;
}

template <class OBJECT_TYPE, class ALLOCATOR>
inline
void AutoArrayDestructor<OBJECT_TYPE, ALLOCATOR>::release()
{
    d_begin_p = d_end_p;
}

}  // close package namespace

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

#ifdef bslalg_AutoArrayDestructor
#undef bslalg_AutoArrayDestructor
#endif
/// This alias is defined for backward compatibility.
#define bslalg_AutoArrayDestructor bslalg::AutoArrayDestructor
#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 ----------------------------------
