// 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
                        // =======================

template <class TYPE, class ALLOCATOR>
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.

    // 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
    RawDeleterProctor(TYPE *object, ALLOCATOR *allocator);
        // 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();
        // 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.

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

    void reset(TYPE *object);
        // 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.
};

// ============================================================================
//                          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 ----------------------------------