// bslma_rawdeleterproctor.h                                          -*-C++-*-
#ifndef INCLUDED_BSLMA_RAWDELETERPROCTOR
#define INCLUDED_BSLMA_RAWDELETERPROCTOR

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

//@PURPOSE: Provide a proctor to conditionally manage an object.
//
//@CLASSES:
//  bslma::RawDeleterProctor: proctor to conditionally manage an object
//
//@SEE_ALSO: bslma_rawdeleterguard, bslma_autorawdeleter
//
//@DESCRIPTION: This component provides a proctor class template,
// `bslma::RawDeleterProctor`, to conditionally manage an (otherwise-unmanaged)
// object of parameterized `TYPE` supplied at construction.  If not explicitly
// released, the managed object is deleted automatically when the proctor
// object goes out of scope by first calling the (managed) object's destructor,
// and then freeing the memory using the parameterized `ALLOCATOR` (allocator
// or pool) also supplied at construction.  Note that after a proctor object
// releases its managed object, the same proctor can be reused to conditionally
// manage another object (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::RawDeleterProctor` 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
///-----
// `bslma::RawDeleterProctor` is normally used to achieve *exception* *safety*
// in an *exception* *neutral* way by managing objects that are created
// temporarily on the heap, but not yet committed to a container object's
// management.  This (somewhat contrived) example illustrates the use of a
// `bslma::RawDeleterProctor` to manage a dynamically-allocated object,
// deleting the object automatically should an exception occur.
//
// Suppose we have a simple linked list class that manages objects of
// parameterized `TYPE`, but which are (for the purpose of this example)
// allocated separately from the links that hold them (thereby requiring two
// separate allocations for each `append` operation):
// ```
// // my_list.h
// // ...
//
// template <class TYPE>
// class my_List {
//     // This class is a container that uses a linked list data structure to
//     // manage objects of parameterized 'TYPE'.
//
//     // PRIVATE TYPES
//     struct Link {
//         TYPE *d_object_p;  // object held by the link
//         Link *d_next_p;    // next link
//     };
//
//     // DATA
//     Link             *d_head_p;       // head of list
//     Link             *d_tail_p;       // tail of list
//     int               d_length;       // number of objects
//     bslma::Allocator *d_allocator_p;  // allocator (held, not owned)
//
//   public:
//     // CREATORS
//     my_List(bslma::Allocator *basicAllocator = 0);
//         // Create a 'my_List' object having an initial length of 0.
//         // Optionally specify a 'basicAllocator' used to supply memory.  If
//         // 'basicAllocator' is 0, the currently installed default allocator
//         // is used.
//
//     // ...
//
//     ~my_List();
//         // Destroy this 'my_List' object and all elements currently stored.
//
//     // MANIPULATORS
//     // ...
//
//     void append(const TYPE& object);
//         // Append (a copy of) the specified 'object' of parameterized
//         // 'TYPE' to (the end of) this list.
//
//     // ...
// };
// ```
// Note that the rest of the `my_List` interface (above) and implementation
// (below) are omitted as the portion shown is sufficient to demonstrate the
// use of `bslma::RawDeleterProctor`.
// ```
// // CREATORS
// template <class TYPE>
// inline
// my_List<TYPE>::my_List(bslma::Allocator *basicAllocator)
// : d_head_p(0)
// , d_tail_p(0)
// , d_length(0)
// , d_allocator_p(bslma::Default::allocator(basicAllocator))
// {
// }
//
// template <class TYPE>
// my_List<TYPE>::~my_List()
// {
//     while (d_head_p) {
//         Link *tmp = d_head_p;
//         d_head_p  = d_head_p->d_next_p;
//         d_allocator_p->deleteObject(tmp->d_object_p);
//         d_allocator_p->deallocate(tmp);
//     }
// }
//
// // MANIPULATORS
// template <class TYPE>
// void my_List<TYPE>::append(const TYPE& object)
// {
//     TYPE *tmp = (TYPE *)new(*d_allocator_p) TYPE(object, d_allocator_p);
//                                                         // possibly throw
//
//     //************************************************************
//     // Note the use of the raw deleter proctor on 'tmp' (below). *
//     //************************************************************
//
//     bslma::RawDeleterProctor<TYPE, bslma::Allocator> proctor(tmp,
//                                                            d_allocator_p);
//
//     if (!d_head_p) {
//         d_head_p           = new(*d_allocator_p) Link;  // possibly throw
//         d_tail_p           = d_head_p;
//     }
//     else {
//         d_tail_p->d_next_p = new(*d_allocator_p) Link;  // possibly throw
//         d_tail_p           = d_tail_p->d_next_p;
//     }
//     d_tail_p->d_object_p   = tmp;
//     d_tail_p->d_next_p     = 0;
//
//     //*********************************************************
//     // Note that the raw deleter proctor is released (below). *
//     //*********************************************************
//
//     proctor.release();
// }
// ```
// The `append` method defined above potentially throws in three places.  If
// the memory allocator held in `d_allocator_p` were to throw while attempting
// to create the object of parameterized `TYPE`, no memory would be leaked.
// But without subsequent use of the `bslma::RawDeleterProctor`, if the
// allocator subsequently throws while creating the link, all memory (and any
// other resources) acquired as a result of copying the (not-yet-managed)
// object would be leaked.  Using the `bslma::RawDeleterProctor` prevents the
// leaks by deleting the proctored object automatically 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).
//
// Note that the `append` 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` uses
// `bslma::Allocator`) should be used (see `bslalg_constructorproxy`).

#include <bslscm_version.h>

#include <bslma_deleterhelper.h>

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

namespace BloombergLP {

namespace bslma {

                        // =======================
                        // class RawDeleterProctor
                        // =======================

/// This class implements a proctor that, unless its `release` method has
/// previously been invoked, automatically deletes a managed object upon
/// destruction by first invoking the object's destructor, and then invoking
/// the `deallocate` method of an allocator (or pool) of parameterized
/// `ALLOCATOR` type supplied to it at construction.  The managed object of
/// parameterized `TYPE` must have been created using memory provided by
/// this allocator (or pool), which must remain valid throughout the
/// lifetime of the proctor object.
template <class TYPE, class ALLOCATOR>
class RawDeleterProctor {

    // DATA
    TYPE      *d_object_p;     // managed object
    ALLOCATOR *d_allocator_p;  // allocator or pool (held, not owned)

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

  public:
    // CREATORS

    /// Create a raw deleter proctor that conditionally manages the
    /// specified `object` (if non-zero), and that uses the specified
    /// `allocator` to delete the object managed by this proctor (if not
    /// released -- see `release`) upon destruction.  The behavior is
    /// undefined unless `allocator` is non-zero and supplied the memory for
    /// `object` (if non-zero).  Note that `allocator` must remain valid
    /// throughout the lifetime of this proctor.
    RawDeleterProctor(TYPE *object, ALLOCATOR *allocator);

    /// Destroy this raw deleter proctor, and delete the object it manages
    /// (if any) by first invoking the destructor of the (managed) object,
    /// and then invoking the `deallocate` method of the allocator (or pool)
    /// that was supplied at the construction of this proctor.  If no object
    /// is currently being managed, this method has no effect.
    ~RawDeleterProctor();

    // MANIPULATORS

    /// Release from management the object currently managed by this
    /// proctor.  If no object is currently being managed, this method has
    /// no effect.
    void release();

    /// Set the specified `object` as the object to be managed by this
    /// proctor.  The behavior is undefined unless `object` is non-zero and
    /// was allocated from the allocator (or pool) supplied at construction.
    /// Note that this method releases any previously-managed object from
    /// management (without deleting it), and so may be invoked with or
    /// without having called `release` when reusing this object.
    void reset(TYPE *object);
};

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

                        // -----------------------
                        // class RawDeleterProctor
                        // -----------------------

// CREATORS
template <class TYPE, class ALLOCATOR>
inline
RawDeleterProctor<TYPE, ALLOCATOR>::
RawDeleterProctor(TYPE *object, ALLOCATOR *allocator)
: d_object_p(object)
, d_allocator_p(allocator)
{
    BSLS_ASSERT_SAFE(allocator);
}

template <class TYPE, class ALLOCATOR>
inline
RawDeleterProctor<TYPE, ALLOCATOR>::~RawDeleterProctor()
{
    BSLS_ASSERT_SAFE(d_allocator_p);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 != d_object_p)) {
        DeleterHelper::deleteObjectRaw(d_object_p, d_allocator_p);
    }
}

// MANIPULATORS
template <class TYPE, class ALLOCATOR>
inline
void RawDeleterProctor<TYPE, ALLOCATOR>::release()
{
    d_object_p = 0;
}

template <class TYPE, class ALLOCATOR>
inline
void RawDeleterProctor<TYPE, ALLOCATOR>::reset(TYPE *object)
{
    BSLS_ASSERT_SAFE(object);

    d_object_p = object;
}

}  // close package namespace
}  // 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 ----------------------------------
