// bslma_destructorproctor.h                                          -*-C++-*-
#ifndef INCLUDED_BSLMA_DESTRUCTORPROCTOR
#define INCLUDED_BSLMA_DESTRUCTORPROCTOR

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

//@PURPOSE: Provide a proctor to conditionally manage an object.
//
//@CLASSES:
//  bslma::DestructorProctor: proctor to conditionally manage an object
//
//@SEE_ALSO: bslma_destructorguard, bslma_autodestructor
//
//@DESCRIPTION: This component provides a proctor class template,
// 'bslma::DestructorProctor', to conditionally manage an (otherwise-unmanaged)
// object of parameterized 'TYPE' supplied at construction.  If not explicitly
// released, the managed object is destroyed automatically when the proctor
// object goes out of scope by calling the object's destructor.  Note that
// after a proctor object releases its managed object, the same proctor can be
// reused to conditionally manage another object by invoking the 'reset'
// method.
//
///Usage
///-----
// The 'bslma::DestructorProctor' is normally used to manage objects that are
// constructed sequentially in a block of memory provided.  This is often the
// case when memory management and primitive helpers are implemented in
// different components.  An example would be the construction of a pair object
// within another container with the help of a scalar primitive helper (see
// 'bslma_constructionutil').  After the first object is constructed in the
// provided memory, it should be protected in case the constructor of the
// second object throws.  The following example illustrates a typical use of
// the 'bslma::DestructorProctor'.
//
// First, suppose we have a pair class similar to 'std::pair':
//..
//  // MyPair.h
//  // ...
//
//  template <class TYPE1, class TYPE2>
//  class MyPair {
//      // This class provides a pair container to pair two different objects,
//      // one of parameterized 'TYPE1', and the other of parameterized
//      // 'TYPE2'.
//
//    public:
//      // PUBLIC TYPES
//      typedef TYPE1 firstType;
//      typedef TYPE2 secondType;
//
//      // PUBLIC DATA
//      TYPE1            first;          // first object
//      TYPE2            second;         // second object
//
//      // Declare trait 'my_PairTrait'.
//      // ...
//
//    public:
//      // CREATORS
//      // ...
//
//      MyPair(const TYPE1&      iFirst,
//             const TYPE2&      iSecond,
//             bslma::Allocator *basic_Allocator = 0)
//          // Create a 'MyPair' object that holds a copy of the specified
//          // 'iFirst' and 'iSecond'.  Optionally specify 'basicAllocator' to
//          // supply memory.  If 'basicAllocator' is zero,  the global default
//          // allocator will be used to supply memory.
//      : first(iFirst)
//      , second(iSecond)
//      , d_allocator_p(bslma::Default::allocator(basic_Allocator))
//      {
//      }
//
//      // ...
//
//  };
//..
// Note that parts of the implementation, including the 'my_PairTrait'
// declaration, are elided.  The 'my_PairTrait' will be used by the primitive
// helper to customize implementations for objects that are pairs.
//
// We now implement the primitive helper:
//..
//  // MyPrimitives.h
//  // ...
//
//  struct MyPrimitives {
//      // This 'struct' provides a namespace for primitive functions used to
//      // construct, destroy, insert, append and remove objects.
//
//    private:
//      // PRIVATE TYPES
//      enum { PAIR_TRAIT = 1, NIL_TRAIT = 0 };
//
//    public:
//      // CLASS METHODS
//
//      template <class TYPE>
//      static void copyConstruct(TYPE             *address,
//                                const TYPE&       original,
//                                bslma::Allocator *basicAllocator);
//          // Copy construct the specified 'original' into the specified
//          // 'address' using the specified 'basicAllocator' (if the
//          // copy constructor of 'TYPE' takes an allocator).
//
//      template <class TYPE>
//      static void copyConstruct(TYPE             *address,
//                                const TYPE&       original,
//                                bslma::Allocator *basicAllocator,
//                                bsl::integral_constant<bool, PAIR_TRAIT> *);
//          // Copy construct the specified 'original' into the specified
//          // 'address' using the specified 'basicAllocator' (if the
//          // copy constructor of 'TYPE' takes an allocator).  Note that
//          // the last parameter is used only for overload resolution.
//
//      template <class TYPE>
//      static void copyConstruct(TYPE             *address,
//                                const TYPE&       original,
//                                bslma::Allocator *basicAllocator,
//                                bsl::integral_constant<bool, NIL_TRAIT> *);
//          // Copy construct the specified 'original' into the specified
//          // 'address' using the specified 'basicAllocator' (if the
//          // copy constructor of 'TYPE' takes an allocator).  Note that
//          // the last parameter is used only for overload resolution.
//  };
//
//  template <class TYPE>
//  inline
//  void MyPrimitives::copyConstruct(TYPE             *address,
//                                   const TYPE&       original,
//                                   bslma::Allocator *basicAllocator)
//  {
//      copyConstruct(address,
//                    original,
//                    basicAllocator,
//                    (typename my_HasPairTrait<TYPE>::type *)0);
//  }
//..
// The implementation of 'copyConstruct' constructs the pair object in two
// steps because of the use of allocators.  We cannot simply pass the allocator
// to the copy constructor of the pair object (since 'std::pair' does not take
// an allocator).  Therefore, we copy construct 'first' and 'second' directly
// in the pair object.
//..
//  template <class TYPE>
//  inline
//  void MyPrimitives::copyConstruct(TYPE             *address,
//                                   const TYPE&       original,
//                                   bslma::Allocator *basicAllocator,
//                                   bsl::integral_constant<int, PAIR_TRAIT> *)
//  {
//      copyConstruct(&address->first, original.first, basicAllocator);
//
//      //**************************************************
//      // Note the use of the destructor proctor (below). *
//      //**************************************************
//
//      bslma::DestructorProctor<typename TYPE::firstType> proctor(
//                                                            &address->first);
//
//      copyConstruct(&address->second, original.second, basicAllocator);
//
//      //********************************************************
//      // Note that the destructor proctor is released (below). *
//      //********************************************************
//
//      proctor.release();
//  }
//
//  template <class TYPE>
//  inline
//  void MyPrimitives::copyConstruct(TYPE             *address,
//                                   const TYPE&       original,
//                                   bslma::Allocator *basicAllocator,
//                                   bsl::integral_constant<int, NIL_TRAIT> *)
//  {
//      new(address)TYPE(original, basicAllocator);
//  }
//..
// Note that the implementation of 'my_HasPairTrait' is not shown.  It is used
// to detect whether 'TYPE' has 'my_PairTrait' or not (see 'bslalg_typetraits',
// 'bslalg_typetraitpair').
//
// In the above implementation, if the copy construction of the second object
// in the pair throws, all memory (and any other resources) acquired as a
// result of copying the (not-yet-managed) object would be leaked.  Using the
// 'bslma::DestructorProctor' prevents the leaks by invoking the destructor of
// 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 'copyConstruct' method assumes the copy constructor of
// 'TYPE::firstType' and 'TYPE::secondType' takes an allocator as a second
// argument.  In production code, a constructor proxy that checks the traits of
// 'TYPE::firstType' and 'TYPE::secondType' (to determine whether they uses
// 'bslma::Allocator') should be used (see 'bslalg_constructorproxy').

#include <bslscm_version.h>

#include <bslma_destructionutil.h>

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

namespace BloombergLP {

namespace bslma {

                        // =======================
                        // class DestructorProctor
                        // =======================

template <class TYPE>
class DestructorProctor {
    // This class implements a proctor that, unless its 'release' method has
    // previously been invoked, automatically destroys a managed object upon
    // destruction by invoking the (managed) object's destructor.

    // DATA
    TYPE *d_object_p;  // managed object

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

  public:
    // CREATORS
    explicit DestructorProctor(TYPE *object);
        // Create a destructor proctor that conditionally manages the specified
        // 'object' (if non-zero) by invoking the destructor of the object
        // managed by this proctor (if not released -- see 'release') upon
        // destruction.

    ~DestructorProctor();
        // Destroy this destructor proctor, and destroy the object it manages
        // (if any) by invoking the destructor of the (managed) object.  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.
        // Note that this method releases any previously-managed object from
        // management (without destroying it), and so may be invoked with or
        // without having called 'release' when reusing this object.
};

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

                        // -----------------------
                        // class DestructorProctor
                        // -----------------------

// CREATORS
template <class TYPE>
inline
DestructorProctor<TYPE>::DestructorProctor(TYPE *object)
: d_object_p(object)
{
}

template <class TYPE>
inline
DestructorProctor<TYPE>::~DestructorProctor()
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 != d_object_p)) {
        DestructionUtil::destroy(d_object_p);
    }
}

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

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

    d_object_p = object;
}

}  // close package namespace

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

#ifdef bslma_DestructorProctor
#undef bslma_DestructorProctor
#endif
#define bslma_DestructorProctor bslma::DestructorProctor
    // 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 ----------------------------------