// 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 // // ... // // template <class TYPE> // class my_Array { // // This class implements an "in-place" array of objects of // // parameterized 'TYPE' stored contiguously in memory. // // // 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 // my_Array(bslma::Allocator *basicAllocator = 0); // // 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(int initialCapacity, 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(); // // Destroy this 'my_Array' object and all elements currently // // stored. // // // MANIPULATORS // // ... // // void insert(int dstIndex, const TYPE& object); // // 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. // // // ... // }; //.. // 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 // ==================== template <class TYPE> 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. // 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 void destroy(); // 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). public: // CREATORS explicit AutoDestructor(TYPE *origin, int length = 0); // 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 //.. ~AutoDestructor(); // 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. // MANIPULATORS void operator++(); // 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 release(); // 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 reset(TYPE *origin); // 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 setLength(int length); // 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. // ACCESSORS int length() const; // Return the (signed) length of the sequence of objects managed by // this range proctor. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // -------------------- // class AutoDestructor // -------------------- // PRIVATE MANIPULATORS template <class TYPE> void AutoDestructor<TYPE>::destroy() { if (0 < d_length) { for (; d_length > 0; --d_length, ++d_origin_p) { d_origin_p->~TYPE(); } } else { --d_origin_p; for (; d_length < 0; ++d_length, --d_origin_p) { d_origin_p->~TYPE(); } } } // 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 #define bslma_AutoDestructor bslma::AutoDestructor // This alias is defined for backward compatibility. #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 ----------------------------------