text: // bslstl_sharedptrallocateoutofplacerep.h                            -*-C++-*-
  #ifndef INCLUDED_BSLSTL_SHAREDPTRALLOCATEOUTOFPLACEREP
  #define INCLUDED_BSLSTL_SHAREDPTRALLOCATEOUTOFPLACEREP
  
  #ifndef INCLUDED_BSLS_IDENT
  #include <bsls_ident.h>
  #endif
  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 'bcema_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 'bdet_Datetime'
  // object using 'bslstl::SharedPtrAllocateOutofplaceRep':
  //..
  //  class MySharedDatetimePtr {
  //      // This class provide a reference counted smart pointer to support
  //      // shared ownership of a 'bdet_Datetime' object.
  //
  //    private:
  //      bdet_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(bdet_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
  //          // 'bdet_Datetime' object to which it might be referring.  If this
  //          // is the last shared reference, deleted the managed object.
  //
  //      // MANIPULATORS
  //      bdet_Datetime& operator*() const;
  //          // Return a reference offering modifiable access to the shared
  //          // datetime.
  //
  //      bdet_Datetime *operator->() const;
  //          // Return the address of the modifiable 'bdet_Datetime' to which
  //          // this object refers.
  //
  //      bdet_Datetime *ptr() const;
  //          // Return the address of the modifiable 'bdet_Datetime' to which
  //          // this object refers.
  //  };
  //..
  // Finally, we define the implementation.
  //..
  //  MySharedDatetimePtr::MySharedDatetimePtr(bdet_Datetime    *ptr,
  //                                           bslma::Allocator *basicAllocator)
  //  {
  //      d_ptr_p = ptr;
  //      d_rep_p = bslstl::SharedPtrAllocateOutofplaceRep<bdet_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();
  //      }
  //  }
  //
  //  bdet_Datetime& MySharedDatetimePtr::operator*() const {
  //      return *d_ptr_p;
  //  }
  //
  //  bdet_Datetime *MySharedDatetimePtr::operator->() const {
  //      return d_ptr_p;
  //  }
  //
  //  bdet_Datetime *MySharedDatetimePtr::ptr() const {
  //      return d_ptr_p;
  //  }
  //..
  
  #ifndef INCLUDED_BSLSCM_VERSION
  #include <bslscm_version.h>
  #endif
  
  #ifndef INCLUDED_BSLSTL_ALLOCATORTRAITS
  #include <bslstl_allocatortraits.h>
  #endif
  
  #ifndef INCLUDED_BSLMA_SHAREDPTRREP
  #include <bslma_sharedptrrep.h>
  #endif
  
  #ifndef INCLUDED_BSLS_UTIL
  #include <bsls_util.h>
  #endif
  
  #ifndef INCLUDED_TYPEINFO
  #include <typeinfo>
  #define INCLUDED_TYPEINFO
  #endif
  
  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 ----------------------------------
  