// bslma_managedptrdeleter.h                                          -*-C++-*-
#ifndef INCLUDED_BSLMA_MANAGEDPTRDELETER
#define INCLUDED_BSLMA_MANAGEDPTRDELETER

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

//@PURPOSE: Provide an in-core value-semantic class to call a delete function.
//
//@CLASSES:
//  bslma::ManagedPtrDeleter: in-core value-semantic class storing a deleter
//
//@SEE_ALSO: bslma_managedptr
//
//@DESCRIPTION: This component provides a single, complex-constrained in-core
// value-semantic attribute class, `bslma::ManagedPtrDeleter`, that is used to
// store a bound function call for a "factory" to destroy an object.
//
///Attributes
///----------
// ```
// Name              Type                      Default
// ----------------  ------------------------  -------
// object            void *                    0
// factory           void *                    0
// deleter           void (*)(void *, void *)  0
//
// Complex Constraints
// ------------------------------------------------------------------
// '0 == deleter' or 'deleter(object, factory)' has defined behavior.
// ```
// * object
//   > Address of the object to be destroyed by the factory.
//
// * factory
//   > Address of the factory object that is responsible for destroying
//   > `object`.
//
// * deleter
//   > Address of the function that restores the erased types of `object` and
//   > `factory`, and invokes the `factory` method to destroy `object`.

#include <bslscm_version.h>

#include <bslmf_isbitwisemoveable.h>

#include <bsls_assert.h>

namespace BloombergLP {
namespace bslma {

                       // =======================
                       // class ManagedPtrDeleter
                       // =======================

/// This complex constrained in-core value-semantic class holds the information
/// necessary for `ManagedPtr` to correctly manage its underlying object,
/// namely the addresses of `object` and `factory`, and the `deleter` function,
/// optionally supplied through the constructors or through the `set` method.
/// This information is stored in a sub-structure to allow the compiler to copy
/// it more efficiently.
///
/// See the [](#Attributes) for information on the class attributes.  Note that
/// the class invariants are identically the constraints on the individual
/// attributes.
///
/// This class:
/// * supports a complete set of *value-semantic* operations
/// * is *exception-safe*
/// * is *alias-safe*
/// * is `const` *thread-safe*
/// For terminology see `bsldoc_glossary`.
class ManagedPtrDeleter {

  public:
    // PUBLIC TYPES

    /// Deleter function prototype used to destroy the managed pointer.
    typedef void (*Deleter)(void *managedObject, void *cookie);

  private:
    // DATA
    void    *d_object_p;   // pointer to the actual managed object (i.e., not
                           // an alias)

    void    *d_factory_p;  // optional factory to be specified to the deleter

    Deleter  d_deleter;    // deleter used to destroy the managed object

  public:
    // CREATORS

    /// Create a default `ManagedPtrDeleter` object that does not refer to any
    /// object or factory instance.
    ManagedPtrDeleter();

    /// Create a `ManagedPtrDeleter` object that refers to the object and
    /// factory instances located at the specified `object` and `factory`
    /// memory locations, and the specified `deleter`.  The behavior is
    /// undefined unless `deleter` is either 0, or points to a function whose
    /// behavior is defined if called once with `object` and `factory` as
    /// arguments.
    ManagedPtrDeleter(void *object, void *factory, Deleter deleter);

    /// Create a `ManagedPtrDeleter` object having the same value as the
    /// specified `original` object.  Note that this trivial copy constructor's
    /// definition is compiler generated.
    //! ManagedPtrDeleter(const ManagedPtrDeleter& original);

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

    // MANIPULATORS

    /// Assign to this object the value of the specified `rhs` object, and
    /// return a reference providing modifiable access to this object.  Note
    /// that this trivial copy-assignment operator's definition is compiler
    /// generated.
    //! ManagedPtrDeleter& operator=(const ManagedPtrDeleter& rhs);

    /// Reset this `ManagedPtrDeleter` to its default-constructed state.
    void clear();

    /// Set this `ManagedPtrDeleter` to refer to the object and factory
    /// instances located at the specified `object` and `factory` memory
    /// locations, and the specified `deleter`.  The behavior is undefined
    /// unless `deleter` is either 0, or points to a function whose behavior is
    /// defined if called once with `object` and `factory` as arguments.
    void set(void *object, void *factory, Deleter deleter);

    // ACCESSORS

    /// Invoke the deleter object.  The behavior is undefined unless `deleter`
    /// is not 0 and has not already been called on the managed object
    /// associated with this deleter.
    void deleteManagedObject() const;

    /// Return the deleter function associated with this deleter.
    Deleter deleter() const;

    /// Return a pointer to the factory instance associated with this deleter.
    void *factory() const;

    /// Return a pointer to the managed object associated with this deleter.
    void *object() const;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` objects have the same value,
/// and `false` otherwise.  Two `ManagedPtrDeleter` objects have the same value
/// if the corresponding values of their `object`, `factory`, and `deleter`
/// attributes are the same.
bool operator==(const ManagedPtrDeleter& lhs, const ManagedPtrDeleter& rhs);

/// Return `true` if the specified `lhs` and `rhs` objects do not have the same
/// value, and `false` otherwise.  Two `ManagedPtrDeleter` objects do not have
/// the same value if any of the corresponding values of their `object`,
/// `factory`, and `deleter` attributes are not the same.
bool operator!=(const ManagedPtrDeleter& lhs, const ManagedPtrDeleter& rhs);

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

                       // -----------------------
                       // class ManagedPtrDeleter
                       // -----------------------

// CREATORS
inline
ManagedPtrDeleter::ManagedPtrDeleter()
: d_object_p(0)
, d_factory_p(0)
, d_deleter(0)
{
}

inline
ManagedPtrDeleter::ManagedPtrDeleter(void    *object,
                                     void    *factory,
                                     Deleter  deleter)
: d_object_p(object)
, d_factory_p(factory)
, d_deleter(deleter)
{
}

// MANIPULATORS
inline
void ManagedPtrDeleter::clear()
{
    d_object_p  = 0;
    d_factory_p = 0;
    d_deleter   = 0;
}

inline
void ManagedPtrDeleter::set(void *object, void *factory, Deleter deleter)
{
    d_object_p  = object;
    d_factory_p = factory;
    d_deleter   = deleter;
}

// ACCESSORS
inline
void ManagedPtrDeleter::deleteManagedObject() const
{
    BSLS_ASSERT_SAFE(0 != d_deleter);

    d_deleter(d_object_p, d_factory_p);
}

inline
ManagedPtrDeleter::Deleter
ManagedPtrDeleter::deleter() const
{
    return d_deleter;
}

inline
void *ManagedPtrDeleter::factory() const
{
    return d_factory_p;
}

inline
void *ManagedPtrDeleter::object() const
{
    return d_object_p;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bslma::operator==(const ManagedPtrDeleter& lhs,
                       const ManagedPtrDeleter& rhs)
{
    return lhs.object()  == rhs.object()
        && lhs.factory() == rhs.factory()
        && lhs.deleter() == rhs.deleter();
}

inline
bool bslma::operator!=(const ManagedPtrDeleter& lhs,
                       const ManagedPtrDeleter& rhs)
{
    return lhs.object()  != rhs.object()
        || lhs.factory() != rhs.factory()
        || lhs.deleter() != rhs.deleter();
}

// TYPE TRAITS
namespace bslmf {

template <>
struct IsBitwiseMoveable<bslma::ManagedPtrDeleter>
    : bsl::integral_constant<bool, true>
{
};

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