// bslma_managedptr_members.h                                         -*-C++-*-
#ifndef INCLUDED_BSLMA_MANAGEDPTR_MEMBERS
#define INCLUDED_BSLMA_MANAGEDPTR_MEMBERS

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

//@PURPOSE: Provide the internal state of a managed pointer class.
//
//@CLASSES:
//  bslma::ManagedPtr_Members: internal state of a 'bslma::ManagedPtr' object
//
//@SEE_ALSO: bslma_managedptr
//
//@DESCRIPTION: This component provides a class, 'bslma::ManagedPtr_Members',
// to store and manage the internal state of a 'bslma::ManagedPtr' object.  It
// enforces the rules for correct transfer of ownership from one
// 'bslma::ManagedPtr' object to another.  A 'bslma::ManagedPtr_Members' object
// has two attributes:
//: o pointer - address of the object being managed, or an alias to the object
//:
//: o deleter - address of a function used to destroy the managed object

#include <bslscm_version.h>

#include <bslma_managedptrdeleter.h>

#include <bslmf_isbitwisemoveable.h>

#include <bsls_assert.h>

namespace BloombergLP {
namespace bslma {

                     // ========================
                     // class ManagedPtr_Members
                     // ========================

class ManagedPtr_Members {
    // This class provides a type-agnostic container for managed pointer data
    // members.  This type exists so that a 'ManagedPtr_Ref' (see
    // 'bslma_managedptr') can point to the representation of a 'ManagedPtr'
    // even if the 'ManagedPtr_Ref' object is instantiated on a different type
    // than the managed pointer type (e.g., in the case of conversions or
    // aliasing).  'ManagedPtr_Members' also "erases" the type of each member,
    // so that the same object code can be shared between all instantiations of
    // the 'ManagedPtr' class template, reducing template bloat.  Note that
    // objects of this type have an "unset" state, where the pointer member has
    // a null value and the deleter member has an unspecified value.

  private:
    // PRIVATE TYPES
    typedef ManagedPtrDeleter::Deleter DeleterFunc;

    // DATA
    void              *d_obj_p;    // pointer to the managed object, or an
                                   // alias to the object; 'void *' is used so
                                   // that this single (non-template) class may
                                   // be used for any instantiation of
                                   // 'bslma::ManagedPtr'

    ManagedPtrDeleter  d_deleter;  // deleter-related information

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

  public:
    // CREATORS
    ManagedPtr_Members();
        // Create a 'ManagedPtr_Members' object in an unset state, i.e., where
        // 'pointer' is null and 'deleter' has an unspecified value.

    explicit ManagedPtr_Members(ManagedPtr_Members& other);
        // Create a 'ManagedPtr_Members' object having the same 'pointer' as
        // the specified 'other' object, and, if '0 != other.pointer()', the
        // same deleter as 'other', then put 'other' into an unset state.

    ManagedPtr_Members(void *object, void *factory, DeleterFunc deleter);
        // Create a 'ManagedPtr_Members' object having the specified 'object',
        // 'factory', and 'deleter'.  The newly created object does not manage
        // a pointer if '0 == object'.  The behavior is undefined unless
        // 'object' is null if 'deleter' is null.

    ManagedPtr_Members(void        *object,
                       void        *factory,
                       DeleterFunc  deleter,
                       void        *alias);
        // Create a 'ManagedPtr_Members' object having the specified 'object',
        // 'factory', 'deleter', and 'alias'.  The newly created object does
        // not manage a pointer if '0 == object'.  The behavior is undefined
        // unless 'object' is null if 'deleter' is null, and '0 == object' if
        // and only if '0 == alias'.  Note that this constructor is important
        // for managed pointers pointing to one of multiple base classes of a
        // class using multiple inheritance.

    //! ~ManagedPtr_Members() = default;
        // Destroy this object.  Note that this trivial destructor's definition
        // is compiler generated.

    // MANIPULATORS
    void clear();
        // Reset this object to an unset state with no effect on the previously
        // managed object (if any).

    void move(ManagedPtr_Members *other);
        // Reset this object to have the same 'pointer' as the specified
        // 'other' object, and, if '0 != other->pointer()', the same deleter as
        // 'other', then put 'other' into an unset state.  This method has no
        // effect on the previously managed object (if any).  The behavior is
        // undefined unless 'this != other'.

    void moveAssign(ManagedPtr_Members *other);
        // Destroy the currently managed object (if any), reset this object to
        // have the same 'pointer' as the specified 'other' object, and, if
        // '0 != other->pointer()', the same deleter as 'other', then put
        // 'other' into an unset state.  This method has no effect if
        // 'this == other'.

    void set(void *object, void *factory, DeleterFunc deleter);
        // Reset this object to have the specified 'object', 'factory', and
        // 'deleter' with no effect on the previously managed object (if any).
        // The object does not manage a pointer if '0 == object'.  The behavior
        // is undefined unless 'object' is null if 'deleter' is null.

    void setAliasPtr(void *alias);
        // Set 'pointer' to have the specified 'alias'.  If '0 == alias', then
        // this object will have an unset state with no effect on the
        // previously managed object (if any).  The behavior is undefined
        // unless '0 == pointer()' if and only if '0 == alias'.

    void swap(ManagedPtr_Members& other);
        // Efficiently exchange the state of this object with the state of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  Note that if either object is in an
        // unset state, then the only guarantee about the unset state that may
        // be exchanged is that the 'pointer' value shall be null.

    // ACCESSORS
    const ManagedPtrDeleter& deleter() const;
        // Return a reference providing non-modifiable access to the deleter
        // used to destroy the currently managed object (if any).  The behavior
        // is undefined unless 'pointer' is not null.

    void *pointer() const;
        // Return the address providing modifiable access to the currently
        // managed object, or 0 if this object is in an unset state.  Note that
        // the returned pointer may be an alias to the managed object.

    void runDeleter() const;
        // Destroy the currently managed object (if any).  Note that calling
        // this method twice on an object that is not in an unset state,
        // without assigning a new pointer to manage between the two calls,
        // results in undefined behavior unless this object's deleter
        // specifically supports such usage.
};

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

                    // ------------------------
                    // class ManagedPtr_Members
                    // ------------------------

// CREATORS
inline
ManagedPtr_Members::ManagedPtr_Members()
: d_obj_p(0)
{
}

inline
ManagedPtr_Members::ManagedPtr_Members(ManagedPtr_Members& other)
: d_obj_p(other.d_obj_p)
{
    if (d_obj_p) {
        d_deleter = other.d_deleter;
    }
    other.clear();
}

inline
ManagedPtr_Members::ManagedPtr_Members(void        *object,
                                       void        *factory,
                                       DeleterFunc  deleter)
: d_obj_p(object)
, d_deleter(object, factory, deleter)
{
    BSLS_ASSERT_SAFE(0 != deleter || 0 == object);
}

inline
ManagedPtr_Members::ManagedPtr_Members(void        *object,
                                       void        *factory,
                                       DeleterFunc  deleter,
                                       void        *alias)
: d_obj_p(alias)
, d_deleter(object, factory, deleter)
{
    BSLS_ASSERT_SAFE(0 != deleter || 0 == object);
    BSLS_ASSERT_SAFE(!object == !alias);  // both are null, or neither is null
}

// MANIPULATORS
inline
void ManagedPtr_Members::clear()
{
    d_obj_p = 0;
}

inline
void ManagedPtr_Members::move(ManagedPtr_Members *other)
{
    BSLS_ASSERT_SAFE(other);
    BSLS_ASSERT_SAFE(this != other);

    // If 'other->d_obj_p' is null, then 'other->d_deleter' has an unspecified
    // value.

    d_obj_p = other->d_obj_p;
    if (other->d_obj_p) {
        d_deleter = other->d_deleter;
    }

    other->clear();
}

inline
void ManagedPtr_Members::moveAssign(ManagedPtr_Members *other)
{
    BSLS_ASSERT_SAFE(other);

    // Must protect against self-assignment due to destructive move.

    if (this != other) {
        runDeleter();
        move(other);
    }
}

inline
void ManagedPtr_Members::set(void *object, void *factory, DeleterFunc deleter)
{
    BSLS_ASSERT_SAFE(0 != deleter || 0 == object);

    // Note that 'factory' may be null if 'deleter' supports it, so that cannot
    // be asserted here.

    d_obj_p = object;
    if (object) {
        d_deleter.set(object, factory, deleter);
    }
}

inline
void ManagedPtr_Members::setAliasPtr(void *alias)
{
    BSLS_ASSERT_SAFE(!alias == !d_obj_p);  // both are null, or neither is null

    d_obj_p = alias;
}

// ACCESSORS
inline
const ManagedPtrDeleter& ManagedPtr_Members::deleter() const
{
    BSLS_ASSERT_SAFE(d_obj_p);

    return d_deleter;
}

inline
void *ManagedPtr_Members::pointer() const
{
    return d_obj_p;
}

inline
void ManagedPtr_Members::runDeleter() const
{
    if (d_obj_p) {
        d_deleter.deleteManagedObject();
    }
}

}  // close package namespace

// TYPE TRAITS
namespace bslmf {

template <>
struct IsBitwiseMoveable<bslma::ManagedPtr_Members> : bsl::true_type
{
};

}  // close namespace bslmf
}  // close enterprise namespace

#endif

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