// bslma_autorawdeleter.h -*-C++-*- #ifndef INCLUDED_BSLMA_AUTORAWDELETER #define INCLUDED_BSLMA_AUTORAWDELETER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a range proctor to manage a sequence objects. // //@CLASSES: // bslma::AutoRawDeleter: range proctor to manage a sequence of objects // //@SEE_ALSO: bslma_rawdeleterproctor, 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::DeallocatorProctor's 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. #include <bslscm_version.h> #include <bslma_deleterhelper.h> #include <bsls_assert.h> #include <bsls_performancehint.h> namespace BloombergLP { namespace bslma { // ==================== // class AutoRawDeleter // ==================== template <class TYPE, class ALLOCATOR> class AutoRawDeleter { // This class implements a range proctor that, unless its 'release' method // has previously been invoked, automatically deletes the contiguous // sequence of managed objects upon destruction by iterating on each // (managed) object, first invoking the object's destructor, and then free // memory by invoking the 'deallocate' method of an allocator (or pool) of // parameterized 'ALLOCATOR' type supplied to it at construction. The // sequence of managed objects of parameterized 'TYPE' must have been // created using memory provided by this allocator (or pool), which must // remain valid throughout the lifetime of this range proctor. 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 sequence of // managed objects int d_length; // number of objects managed (sign encodes // direction) ALLOCATOR *d_allocator_p; // allocator or pool (held, not owned) // NOT IMPLEMENTED AutoRawDeleter(const AutoRawDeleter&); AutoRawDeleter& operator=(const AutoRawDeleter&); private: // PRIVATE MANIPULATORS void rawDelete(); // Delete the contiguous sequence of objects managed by this auto raw // deleter (if any) by iterating over each (managed) object, first // invoking the object's destructor, and then freeing memory by // invoking the 'deallocate' method of the allocator (or pool) that was // supplied with the sequence of (managed) objects at construction. // Note that the order in which the managed objects are deleted 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 released before being destroyed). public: // CREATORS AutoRawDeleter(TYPE **origin, ALLOCATOR *allocator, int length = 0); // Create an auto raw deleter to manage an array of objects at the // specified 'origin', and that uses the specified 'allocator' to // delete the sequence of objects managed by this range proctor (if not // released -- see 'release') upon destruction. 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. If 'origin' is non-zero, all objects // within the proctored range (if any) must be constructed using memory // supplied by 'allocator'. The behavior is undefined unless // 'allocator' is non-zero, and, if 'origin' is 0, 'length is also 0. // 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 //.. ~AutoRawDeleter(); // Destroy this range proctor and delete the contiguous sequence of // objects it manages (if any) by iterating over each (managed) object, // first invoking the object's destructor, and then freeing memory by // invoking the 'deallocate' method of the allocator (or pool) that was // supplied with the sequence of (managed) objects at construction. // Note that the order in which the managed objects are deleted 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 or 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 or 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 deleted). 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' as the origin of the sequence of objects // to be managed by this range proctor. The behavior is undefined // unless 'origin' is non-zero. Note that the length of the sequence // of objects managed by this proctor is not affected, and 'setLength' // should be invoked if the managed range is different from the // previously managed sequence of objects. Also note that this method // releases any previously-managed objects from management (without // deleting them), and so may be called with or without having called // 'release' when reusing this object. 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 AutoRawDeleter // -------------------- // PRIVATE MANIPULATORS template <class TYPE, class ALLOCATOR> void AutoRawDeleter<TYPE, ALLOCATOR>::rawDelete() { if (d_length > 0) { for (; d_length > 0; --d_length, ++d_origin_p) { DeleterHelper::deleteObjectRaw(*d_origin_p, d_allocator_p); } } else { --d_origin_p; for (; d_length < 0; ++d_length, --d_origin_p) { DeleterHelper::deleteObjectRaw(*d_origin_p, d_allocator_p); } } } // CREATORS template <class TYPE, class ALLOCATOR> inline AutoRawDeleter<TYPE, ALLOCATOR>:: AutoRawDeleter(TYPE **origin, ALLOCATOR *allocator, int length) : d_origin_p(origin) , d_length(length) , d_allocator_p(allocator) { BSLS_ASSERT_SAFE(allocator); BSLS_ASSERT_SAFE(origin || !length); } template <class TYPE, class ALLOCATOR> inline AutoRawDeleter<TYPE, ALLOCATOR>::~AutoRawDeleter() { BSLS_ASSERT_SAFE(d_origin_p || !d_length); if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_length)) { rawDelete(); } } // MANIPULATORS template <class TYPE, class ALLOCATOR> inline void AutoRawDeleter<TYPE, ALLOCATOR>::operator++() { BSLS_ASSERT_SAFE(d_origin_p); ++d_length; } template <class TYPE, class ALLOCATOR> inline void AutoRawDeleter<TYPE, ALLOCATOR>::operator--() { BSLS_ASSERT_SAFE(d_origin_p); --d_length; } template <class TYPE, class ALLOCATOR> inline void AutoRawDeleter<TYPE, ALLOCATOR>::release() { d_length = 0; } template <class TYPE, class ALLOCATOR> inline void AutoRawDeleter<TYPE, ALLOCATOR>::reset(TYPE **origin) { BSLS_ASSERT_SAFE(origin); d_origin_p = origin; } template <class TYPE, class ALLOCATOR> inline void AutoRawDeleter<TYPE, ALLOCATOR>::setLength(int length) { BSLS_ASSERT_SAFE(d_origin_p); d_length = length; } // ACCESSORS template <class TYPE, class ALLOCATOR> inline int AutoRawDeleter<TYPE, ALLOCATOR>::length() const { return d_length; } } // close package namespace #ifndef BDE_OPENSOURCE_PUBLICATION // BACKWARD_COMPATIBILITY // ============================================================================ // BACKWARD COMPATIBILITY // ============================================================================ #ifdef bslma_AutoRawDeleter #undef bslma_AutoRawDeleter #endif #define bslma_AutoRawDeleter bslma::AutoRawDeleter // 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 ----------------------------------