// bslstl_sharedptrallocateoutofplacerep.h                            -*-C++-*-
#ifndef INCLUDED_BSLSTL_SHAREDPTRALLOCATEOUTOFPLACEREP
#define INCLUDED_BSLSTL_SHAREDPTRALLOCATEOUTOFPLACEREP

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

//@PURPOSE: Provide an out-of-place implementation of 'bslma::SharedPtrRep'.
//
//@CLASSES:
//  bslstl::SharedPtrAllocateOutofplaceRep: out-of-place 'shared_ptr' imp.
//
//@SEE_ALSO: bslma_sharedptrrep, bslma_sharedptroutofplacerep, bslstl_sharedptr
//
//@DESCRIPTION: This component provides a class template,
// 'bslstl::SharedPtrAllocateOutofplaceRep' , which is a concrete
// implementation of 'bslma::SharedPtrRep' for managing objects of the
// parameterized 'TYPE' that are stored outside of the representation.  When
// all references to the out-of-place object are released using 'releaseRef',
// the deleter of the parameterized 'DELETER' type is invoked to delete the
// shared object.  Memory is supplied and reclaimed by an allocator of the
// parameterized 'ALLOCATOR' type.
//
///Thread Safety
///-------------
// 'bslstl::SharedPtrAllocateOutofplaceRep' is thread-safe provided that
// 'disposeObject' and 'disposeRep' are not called explicitly, meaning that all
// non-creator operations other than 'disposeObject' and 'disposeRep' on a
// given instance can be safely invoked simultaneously from multiple threads
// ('disposeObject' and 'disposeRep' are meant to be invoked only by
// 'releaseRef' and 'releaseWeakRef').  Note that there is no thread safety
// guarantees for operations on the managed object.
//
///Deleters
///--------
// When the last shared reference to a shared object is released, the object is
// destroyed using the "deleter" provided when the associated shared pointer
// representation was created.  'bslstl::SharedPtrAllocateOutofplaceRep'
// supports two kinds of "deleter" objects, which vary in how they are invoked.
// A "function-like" deleter is any language entity that can be invoked such
// that the expression 'deleterInstance(objectPtr)' is a valid expression, and
// a "factory" deleter is any language entity that can be invoked such that the
// expression 'deleterInstance.deleteObject(objectPtr)' is a valid expression,
// where 'deleterInstance' is an instance of the "deleter" object, and
// 'objectPtr' is a pointer to the shared object.  In summary:
//..
//  Deleter                     Expression used to destroy 'objectPtr'
//  - - - - - - - -             - - - - - - - - - - - - - - - - - - -
//  "function-like"             deleterInstance(objectPtr);
//  "factory"                   deleterInstance.deleteObject(objectPtr);
//..
// The following are examples of function-like deleters that delete an object
// of 'MyType':
//..
//  void deleteObject(MyType *object);
//      // Delete the specified 'object'.
//
//  void releaseObject(MyType *object);
//      // Release the specified 'object'.
//
//  struct FunctionLikeDeleterObject {
//      // This 'struct' provides an 'operator()' that can be used to delete a
//      // 'MyType' object.
//
//      void operator()(MyType *object);
//          // Destroy the specified 'object'.
//  };
//..
// The following on the other hand is an example of a factory deleter:
//..
//  class MyFactory {
//
//     // . . .
//
//     // MANIPULATORS
//     MyType *createObject(bslma::Allocator *basicAllocator = 0);
//         // Create a 'MyType' object.  Optionally specify a 'basicAllocator'
//         // used to supply memory.  If 'basicAllocator' is 0, the currently
//         // installed default allocator is used.
//
//     void deleteObject(MyType *object);
//         // Delete the specified 'object'.
//  };
//..
// Note that 'deleteObject' is provided by all 'bslma' allocators and by any
// object that implements the 'bdlma::Deleter' protocol.  Thus, any of these
// objects can be used as a factory deleter.  The purpose of this design is to
// allow 'bslma' allocators and factories to be used seamlessly as deleters.
//
// The selection of which expression is used by
// 'bslstl::SharedPtrAllocateOutofplaceRep' to destroy a shared object is based
// on how the deleter is passed to the shared pointer object: Deleters that are
// passed by *address* are assumed to be factory deleters, while those that are
// passed by *value* are assumed to be function-like.  Note that if the wrong
// interface is used for a deleter, i.e., if a function-like deleter is passed
// by pointer, or a factory deleter is passed by value, and the expression used
// to delete the object is invalid, a compiler diagnostic will be emitted
// indicating the error.
//
///Usage
///-----
// The following example demonstrates how to implement a shared
// 'bdlt::Datetime' object using 'bslstl::SharedPtrAllocateOutofplaceRep':
//..
//  class MySharedDatetimePtr {
//      // This class provide a reference counted smart pointer to support
//      // shared ownership of a 'bdlt::Datetime' object.
//
//    private:
//      bdlt::Datetime      *d_ptr_p;  // pointer to the managed object
//      bslma::SharedPtrRep *d_rep_p;  // pointer to the representation object
//
//    private:
//      // NOT IMPLEMENTED
//      MySharedDatetimePtr& operator=(const MySharedDatetimePtr&);
//
//    public:
//      // CREATORS
//      MySharedDatetimePtr(bdlt::Datetime   *ptr,
//                          bslma::Allocator *basicAllocator = 0);
//          // Create a 'MySharedDatetimePtr' object to managed the specified
//          // 'ptr'.  Optionally specify an 'basicAllocator' to allocate and
//          // deallocate the internal representation and to destroy 'ptr' when
//          // all references have been released.  The behavior is undefined
//          // unless 'ptr' was allocated using memory supplied by
//          // 'basicAllocator'.
//
//      MySharedDatetimePtr(const MySharedDatetimePtr& original);
//          // Create a shared datetime that refers to the same object managed
//          // by the specified 'original'
//
//      ~MySharedDatetimePtr();
//          // Destroy this shared datetime and release the reference to the
//          // 'bdlt::Datetime' object to which it might be referring.  If this
//          // is the last shared reference, deleted the managed object.
//
//      // MANIPULATORS
//      bdlt::Datetime& operator*() const;
//          // Return a reference offering modifiable access to the shared
//          // datetime.
//
//      bdlt::Datetime *operator->() const;
//          // Return the address of the modifiable 'bdlt::Datetime' to which
//          // this object refers.
//
//      bdlt::Datetime *ptr() const;
//          // Return the address of the modifiable 'bdlt::Datetime' to which
//          // this object refers.
//  };
//..
// Finally, we define the implementation.
//..
//  MySharedDatetimePtr::MySharedDatetimePtr(bdlt::Datetime   *ptr,
//                                           bslma::Allocator *basicAllocator)
//  {
//      d_ptr_p = ptr;
//      d_rep_p = bslstl::SharedPtrAllocateOutofplaceRep<bdlt::Datetime,
//                                                       bslma::Allocator *>::
//                      makeOutofplaceRep(ptr, basicAllocator, basicAllocator);
//  }
//
//  MySharedDatetimePtr::MySharedDatetimePtr(
//                                         const MySharedDatetimePtr& original)
//  : d_ptr_p(original.d_ptr_p)
//  , d_rep_p(original.d_rep_p)
//  {
//      if (d_ptr_p) {
//          d_rep_p->acquireRef();
//      } else {
//          d_rep_p = 0;
//      }
//  }
//
//  MySharedDatetimePtr::~MySharedDatetimePtr()
//  {
//      if (d_rep_p) {
//          d_rep_p->releaseRef();
//      }
//  }
//
//  bdlt::Datetime& MySharedDatetimePtr::operator*() const {
//      return *d_ptr_p;
//  }
//
//  bdlt::Datetime *MySharedDatetimePtr::operator->() const {
//      return d_ptr_p;
//  }
//
//  bdlt::Datetime *MySharedDatetimePtr::ptr() const {
//      return d_ptr_p;
//  }
//..

#include <bslscm_version.h>

#include <bslma_allocatortraits.h>
#include <bslma_sharedptrrep.h>

#include <bsls_util.h>

#include <typeinfo>

namespace BloombergLP {
namespace bslstl {

                 // ====================================
                 // class SharedPtrAllocateOutofplaceRep
                 // ====================================

template <class TYPE, class DELETER, class ALLOCATOR>
class SharedPtrAllocateOutofplaceRep : public BloombergLP::bslma::SharedPtrRep
                                                                              {
    // This class provides a concrete implementation of the 'SharedPtrRep'
    // protocol for out-of-place instances of the parameterized 'TYPE'.  Upon
    // destruction of this object, the parameterized 'DELETER' type is invoked
    // on the pointer to the shared object.

    // PRIVATE TYPES
    typedef bsl::allocator_traits<ALLOCATOR> OriginalTraits;

    typedef typename
         OriginalTraits::template rebind_traits<SharedPtrAllocateOutofplaceRep>
                                                               AllocatorTraits;
    typedef typename AllocatorTraits::allocator_type Allocator;

    // DATA
    TYPE      *d_ptr_p;     // pointer to out-of-place object (held, not owned)
    DELETER    d_deleter;   // deleter for this out-of-place instance
    Allocator  d_allocator; // copy of the allocator for this instance

  private:
    // NOT IMPLEMENTED
    SharedPtrAllocateOutofplaceRep(const SharedPtrAllocateOutofplaceRep&);
    SharedPtrAllocateOutofplaceRep& operator=(
                                        const SharedPtrAllocateOutofplaceRep&);

    // PRIVATE CREATORS
    SharedPtrAllocateOutofplaceRep(TYPE             *ptr,
                                   const DELETER&    deleter,
                                   const ALLOCATOR&  basicAllocator);
        // Create a 'SharedPtrAllocateOutofplaceRep' that manages the lifetime
        // of the specified 'ptr', using the specified 'deleter' to destroy
        // 'ptr', and using the specified 'basicAllocator' to supply memory.
        // Note that 'basicAllocator' will be used to destroy this
        // representation object, but not necessarily to destroy 'ptr'.  Also
        // note that a 'SharedPtrAllocateOutofplaceRep' must be created using
        // 'makeOutofplaceRep', which will call the private constructor.

    ~SharedPtrAllocateOutofplaceRep();
        // Destroy this representation object and if the shared object has not
        // been deleted, delete the shared object using the associated deleter.
        // Note that this destructor is never called explicitly.  Instead,
        // 'disposeObject' destroys the shared object and 'disposeRep'
        // deallocates this representation object.

  public:
    // CLASS METHODS
    static SharedPtrAllocateOutofplaceRep *makeOutofplaceRep(
                                             TYPE             *ptr,
                                             const DELETER&    deleter,
                                             const ALLOCATOR&  basicAllocator);
        // Return the address of a newly created
        // 'SharedPtrAllocateOutofplaceRep' object that manages the lifetime of
        // the specified 'ptr', uses the specified 'deleter' to destroy 'ptr',
        // and uses the specified 'basicAllocator' to supply memory.  Note that
        // the parameterized 'DELETER' type will be used to deallocate the
        // memory pointed to by 'ptr'.

    // MANIPULATORS
    virtual void disposeObject();
        // Destroy the object referred to by this representation.  This method
        // is invoked by 'releaseRef' when the number of shared references
        // reaches zero and should not be explicitly invoked otherwise.

    virtual void disposeRep();
        // Destroy this representation object and deallocate the associated
        // memory.  This method is invoked by 'releaseRef' and 'releaseWeakRef'
        // when the number of weak references and the number of shared
        // references both reach zero and should not be explicitly invoked
        // otherwise.  The behavior is undefined unless 'disposeObject' has
        // already been called for this representation.  Note that this method
        // effectively serves as the representation object's destructor.

    virtual void *getDeleter(const std::type_info& type);
        // Return a pointer to the deleter stored by the derived representation
        // if the deleter has the same type as that described by the specified
        // 'type', and a null pointer otherwise.

    // ACCESSORS
    virtual void *originalPtr() const;
        // Return the (untyped) address of the modifiable shared object to
        // which this object refers.

    TYPE *ptr() const;
        // Return the address of the modifiable shared object to which this
        // object refers.
};

               // =================================================
               // struct SharedPtrAllocateOutofplaceRep_InitProctor
               // =================================================

template <class TYPE, class DELETER>
class SharedPtrAllocateOutofplaceRep_InitProctor {
    // This proctor is used for out-of-place shared pointer instantiations.
    // Generally, a proctor is created prior to constructing a
    // 'SharedPtrAllocateOutofplaceRep' and released after successful
    // construction.  In the event that an exception is thrown during
    // construction of the representation, the proctor will delete the provided
    // pointer using the provided deleter.  Note that the provided deleter is
    // held by reference and must remain valid for the lifetime of the proctor.
    // If the proctor is not released before it's destruction, a copy of the
    // deleter is instantiated to delete the pointer (in case 'operator()' is
    // non-'const').  Also note that if the deleter throws during
    // copy construction, the provided pointer will not be destroyed.

    // DATA
    TYPE           *d_ptr_p;    // address of the managed object (held, not
                                // owned)

    const DELETER&  d_deleter;  // deleter used to destroy managed object

  private:
    // NOT IMPLEMENTED
    SharedPtrAllocateOutofplaceRep_InitProctor(
                const SharedPtrAllocateOutofplaceRep_InitProctor&); // = delete
    SharedPtrAllocateOutofplaceRep_InitProctor& operator=(
                const SharedPtrAllocateOutofplaceRep_InitProctor&); // = delete

  public:
    // CREATORS
    SharedPtrAllocateOutofplaceRep_InitProctor(TYPE           *ptr,
                                               const DELETER&  deleter);
        // Create a proctor managing the specified 'ptr' and using the
        // specified 'deleter' to destroy 'ptr' when the proctor is destroyed,
        // unless it has been released from management by a call to 'release'.

    ~SharedPtrAllocateOutofplaceRep_InitProctor();
        // Destroy this proctor and the object (if any) managed by this
        // proctor.

    // MANIPULATORS
    void release();
        // Release from management the object referred to by this proctor.
};

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

                  // ------------------------------------
                  // class SharedPtrAllocateOutofplaceRep
                  // ------------------------------------

// PRIVATE CREATORS
template <class TYPE, class DELETER, class ALLOCATOR>
inline
SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::
SharedPtrAllocateOutofplaceRep(TYPE             *ptr,
                               const DELETER&    deleter,
                               const ALLOCATOR&  basicAllocator)
: d_ptr_p(ptr)
, d_deleter(deleter)
, d_allocator(basicAllocator)
{
}

template <class TYPE, class DELETER, class ALLOCATOR>
inline
SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::
~SharedPtrAllocateOutofplaceRep()
{
}

// CLASS METHODS
template <class TYPE, class DELETER, class ALLOCATOR>
SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR> *
SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::makeOutofplaceRep(
                                              TYPE             *ptr,
                                              const DELETER&    deleter,
                                              const ALLOCATOR&  basicAllocator)
{
    SharedPtrAllocateOutofplaceRep_InitProctor<TYPE, DELETER> proctor(ptr,
                                                                      deleter);

    Allocator alloc(basicAllocator);

    SharedPtrAllocateOutofplaceRep *rep = AllocatorTraits::allocate(alloc, 1);
    new (rep) SharedPtrAllocateOutofplaceRep(ptr, deleter, alloc);

    proctor.release();

    return rep;
}

// MANIPULATORS
template <class TYPE, class DELETER, class ALLOCATOR>
inline
void SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::disposeObject()
{
    d_deleter(d_ptr_p);
    d_ptr_p = 0;
}

template <class TYPE, class DELETER, class ALLOCATOR>
inline
void SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::disposeRep()
{
    Allocator alloc(d_allocator);
    this->~SharedPtrAllocateOutofplaceRep();
    AllocatorTraits::deallocate(alloc, this, 1);
}

template <class TYPE, class DELETER, class ALLOCATOR>
inline
void *
SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::getDeleter(
                                                    const std::type_info& type)
{
    return typeid(d_deleter) == type
         ? bsls::Util::addressOf(d_deleter)
         : 0;
}

// ACCESSORS
template <class TYPE, class DELETER, class ALLOCATOR>
inline
void *
SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::originalPtr() const
{
    return const_cast<void *>(static_cast<const void *>(d_ptr_p));
}

template <class TYPE, class DELETER, class ALLOCATOR>
inline
TYPE *SharedPtrAllocateOutofplaceRep<TYPE, DELETER, ALLOCATOR>::ptr() const
{
    return d_ptr_p;
}

               // ------------------------------------------
               // SharedPtrAllocateOutofplaceRep_InitProctor
               // ------------------------------------------

// CREATORS
template <class TYPE, class DELETER>
inline
SharedPtrAllocateOutofplaceRep_InitProctor<TYPE, DELETER>::
SharedPtrAllocateOutofplaceRep_InitProctor(TYPE           *ptr,
                                           const DELETER&  deleter)
: d_ptr_p(ptr)
, d_deleter(deleter)
{
}

template <class TYPE, class DELETER>
inline
SharedPtrAllocateOutofplaceRep_InitProctor<TYPE, DELETER>::
~SharedPtrAllocateOutofplaceRep_InitProctor()
{
    // The proctor must destroy 'd_ptr_p' to avoid a leak, but is not subject
    // to the reference-counting of null pointers principle, where the deleter
    // is called on destroying the last reference, even if 'd_ptr_p' is null.

    if (d_ptr_p) {
        d_deleter(d_ptr_p);
    }
}

// MANIPULATORS
template <class TYPE, class DELETER>
inline
void SharedPtrAllocateOutofplaceRep_InitProctor<TYPE, DELETER>::release()
{
    d_ptr_p = 0;
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2014 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 ----------------------------------