// bsltf_allocargumenttype.h                                          -*-C++-*-
#ifndef INCLUDED_BSLTF_ALLOCARGUMENTTYPE
#define INCLUDED_BSLTF_ALLOCARGUMENTTYPE

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

//@PURPOSE: Provide an allocating test class to represent function arguments.
//
//@CLASSES:
//   bsltf::AllocArgumentType<N>: simple wrapper around an allocated `int`
//
//@SEE_ALSO: bsltf_argumenttype, bsltf_templatetestfacility
//
//@DESCRIPTION: This component provides a representation of an allocating
// argument type template class, `bsltf::AllocArgumentType<N>`, used for
// testing functions that take a variable number of template arguments.  The
// integer template parameter enables specification of a number of types
// without requiring a separate component for each.  Note that
// default-constructed object of this type does not allocate any memory. Also
// note, that copy or move of the default-constructed object also does not
// allocate any memory.  Copy and move constructors are defined.
//
///Attributes
///----------
// ```
// Name                Type         Default
// ------------------  -----------  -------
// data                int          -1
// ```
// * `data`: representation of the class value
//
///Usage
///-----
// This section illustrates intended use of this component.
//
// See `bsltf_argumenttype` for usage example.

#include <bslscm_version.h>

#include <bsltf_movestate.h>

#include <bslma_default.h>
#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_movableref.h>

#include <bsls_assert.h>

namespace BloombergLP {
namespace bsltf {

                         // ============================
                         // class AllocArgumentType<int>
                         // ============================

/// This class template declares a separate type for each template parameter
/// value `N`, `bsltf::AllocArgumentType<N>`, that wraps an integer value
/// and provides implicit conversion to and from `int`.  The wrapped integer
/// will be dynamically allocated using the supplied allocator, or the
/// default allocator if none is supplied.  Its main purpose is that having
/// separate types for testing enables distinguishing them when calling
/// through a function template interface, thereby avoiding ambiguities or
/// accidental switching of arguments in the implementation of in-place
/// constructors.  It further tests that allocators are propagated
/// correctly, or not, as required.
template <int N>
class AllocArgumentType {

    // PRIVATE TYPES
    typedef bslmf::MovableRefUtil MoveUtil;

    // DATA
    bslma::Allocator *d_allocator_p;        // allocator used to supply memory
                                            // (held, not owned)

    int              *d_data_p;             // pointer to the data value

    MoveState::Enum   d_movedFrom;          // moved-from state

    MoveState::Enum   d_movedInto;          // moved-from state

  public:
    // CREATORS

    /// Create an `AllocArgumentType` object having the (default) attribute
    /// value `-1`.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used. Note, that default constructor does not allocate
    /// memory.
    explicit AllocArgumentType(bslma::Allocator *basicAllocator =  0);

    /// Create an `AllocArgumentType` object having the specified `value`.
    /// Optionally specify a `basicAllocator` used to supply memory.  If
    /// `basicAllocator` is 0, the currently installed default allocator is
    /// used. The behavior is undefined unless `value >= 0`.
    explicit AllocArgumentType(int               value,
                               bslma::Allocator *basicAllocator =  0);

    /// Create an `AllocArgumentType` object having the same value as the
    /// specified `original`.  Optionally specify a `basicAllocator` used to
    /// supply memory.  If `basicAllocator` is 0, the currently installed
    /// default allocator is used. Note, that no memory is allocated if
    /// `original` refers to a default-constructed object.
    AllocArgumentType(const AllocArgumentType&  original,
                      bslma::Allocator         *basicAllocator = 0);

    /// Create an `AllocArgumentType` object having the same value as the
    /// specified `original`. Note, that no memory is allocated if
    /// `original` refers to a default-constructed object.
    AllocArgumentType(bslmf::MovableRef<AllocArgumentType> original);

    /// Create an `AllocArgumentType` object having the same value as the
    /// specified `original` using the specified `basicAllocator` to supply
    /// memory.  Note, that no memory is allocated if `original` refers to a
    /// default-constructed object.
    AllocArgumentType(bslmf::MovableRef<AllocArgumentType>  original,
                      bslma::Allocator                     *basicAllocator);

    /// Destroy this object.
    ~AllocArgumentType();

    // MANIPULATORS

    /// Assign to this object the value of the specified `rhs` object.
    AllocArgumentType& operator=(const AllocArgumentType& rhs);

    /// Assign to this object the value of the specified `rhs` object.  Note
    /// that `rhs` is left in a valid but unspecified state.
    AllocArgumentType&
    operator=(BloombergLP::bslmf::MovableRef<AllocArgumentType> rhs);

    // ACCESSORS

    /// Return the allocator used by this object to supply memory.
    bslma::Allocator *allocator() const;

    /// Return the value of this test argument object.
    operator int() const;

    /// Return the move state of this object as target of a move operation.
    MoveState::Enum movedInto() const;

    /// Return the move state of this object as source of a move operation.
    MoveState::Enum movedFrom() const;
};

// FREE FUNCTIONS

/// Return the move-from state of the specified `object`.
template <int N>
MoveState::Enum getMovedFrom(const AllocArgumentType<N>& object);

/// Return the move-into state of the specified `object`.
template <int N>
MoveState::Enum getMovedInto(const AllocArgumentType<N>& object);

// ============================================================================
//                  INLINE AND TEMPLATE FUNCTION IMPLEMENTATIONS
// ============================================================================

                        // --------------------------
                        // class AllocArgumentType<N>
                        // --------------------------

// CREATORS
template <int N>
inline
AllocArgumentType<N>::AllocArgumentType(bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
, d_data_p(0)
, d_movedFrom(bsltf::MoveState::e_NOT_MOVED)
, d_movedInto(bsltf::MoveState::e_NOT_MOVED)
{
    // Note that the default constructor does not allocate.  This is done to
    // correctly count allocations when not the whole set of arguments is
    // passed to emplacable test types.
}

template <int N>
inline
AllocArgumentType<N>::AllocArgumentType(int               value,
                                        bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
, d_data_p(0)
, d_movedFrom(bsltf::MoveState::e_NOT_MOVED)
, d_movedInto(bsltf::MoveState::e_NOT_MOVED)
{
    BSLS_ASSERT_SAFE(value >= 0);

    d_data_p = reinterpret_cast<int *>(d_allocator_p->allocate(sizeof(int)));
    *d_data_p = value;
}

template <int N>
inline
AllocArgumentType<N>::AllocArgumentType(
                                      const AllocArgumentType&  original,
                                      bslma::Allocator         *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
, d_data_p(0)
, d_movedFrom(bsltf::MoveState::e_NOT_MOVED)
, d_movedInto(bsltf::MoveState::e_NOT_MOVED)
{
    if (original.d_data_p) {
        d_data_p = reinterpret_cast<int *>(
                                         d_allocator_p->allocate(sizeof(int)));
        *d_data_p = int(original);
    }
}

template <int N>
inline
AllocArgumentType<N>::AllocArgumentType(
                                 bslmf::MovableRef<AllocArgumentType> original)
: d_allocator_p(MoveUtil::access(original).d_allocator_p)
, d_movedFrom(bsltf::MoveState::e_NOT_MOVED)
, d_movedInto(bsltf::MoveState::e_MOVED)
{
    AllocArgumentType& lvalue = original;

    d_data_p = lvalue.d_data_p;

    lvalue.d_data_p = 0;
    lvalue.d_movedInto = bsltf::MoveState::e_NOT_MOVED;
    lvalue.d_movedFrom = bsltf::MoveState::e_MOVED;
}

template <int N>
inline
AllocArgumentType<N>::AllocArgumentType(
                          bslmf::MovableRef<AllocArgumentType>  original,
                          bslma::Allocator                     *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
, d_movedFrom(bsltf::MoveState::e_NOT_MOVED)
, d_movedInto(bsltf::MoveState::e_MOVED)
{
    AllocArgumentType& lvalue = original;

    if (d_allocator_p == lvalue.d_allocator_p) {
        d_data_p = lvalue.d_data_p;
        lvalue.d_data_p = 0;
    } else {
        if (lvalue.d_data_p) {
            d_data_p  = reinterpret_cast<int *>(
                                         d_allocator_p->allocate(sizeof(int)));
            *d_data_p = int(lvalue);
            *lvalue.d_data_p = -1;
        } else {
            d_data_p = 0;
        }
    }

    lvalue.d_movedInto = bsltf::MoveState::e_NOT_MOVED;
    lvalue.d_movedFrom = bsltf::MoveState::e_MOVED;
}

template <int N>
inline
AllocArgumentType<N>::~AllocArgumentType()
{
    if (d_data_p) {
        d_allocator_p->deallocate(d_data_p);
    }
}

// MANIPULATORS
template <int N>
inline
AllocArgumentType<N>&
AllocArgumentType<N>::operator=(const AllocArgumentType& rhs)
{
    if (this != &rhs) {
        int *newValue = 0;
        if (rhs.d_data_p) {
            newValue = reinterpret_cast<int *>(
                                         d_allocator_p->allocate(sizeof(int)));
            *newValue = int(rhs);
        }
        if (d_data_p) {
            d_allocator_p->deallocate(d_data_p);
        }
        d_data_p = newValue;

        d_movedInto = bsltf::MoveState::e_NOT_MOVED;
        d_movedFrom = bsltf::MoveState::e_NOT_MOVED;
    }
    return *this;
}

template <int N>
inline
AllocArgumentType<N>& AllocArgumentType<N>::operator=(
                         BloombergLP::bslmf::MovableRef<AllocArgumentType> rhs)
{
    AllocArgumentType& lvalue = rhs;

    if (this != &lvalue) {
        if (d_allocator_p == lvalue.d_allocator_p) {
            if (d_data_p) {
                d_allocator_p->deallocate(d_data_p);
            }
            d_data_p = lvalue.d_data_p;
            lvalue.d_data_p = 0;
        }
        else {
            int *newValue = 0;
            if (lvalue.d_data_p) {
                newValue = reinterpret_cast<int *>(
                                         d_allocator_p->allocate(sizeof(int)));
                *newValue = int(lvalue);
                *lvalue.d_data_p = -1;
            }
            if (d_data_p) {
                d_allocator_p->deallocate(d_data_p);
            }
            d_data_p = newValue;
        }

        d_movedInto = bsltf::MoveState::e_MOVED;
        d_movedFrom = bsltf::MoveState::e_NOT_MOVED;

        lvalue.d_movedInto = bsltf::MoveState::e_NOT_MOVED;
        lvalue.d_movedFrom = bsltf::MoveState::e_MOVED;
    }
    return *this;
}

// ACCESSORS
template <int N>
inline
AllocArgumentType<N>::operator int() const
{
    return d_data_p ? *d_data_p : -1;
}

template <int N>
inline
bslma::Allocator * AllocArgumentType<N>::allocator() const
{
    return d_allocator_p;
}

template <int N>
inline
MoveState::Enum AllocArgumentType<N>::movedFrom() const
{
    return d_movedFrom;
}

template <int N>
inline
MoveState::Enum AllocArgumentType<N>::movedInto() const
{
    return d_movedInto;
}

                                  // Aspects

// FREE FUNCTIONS
template <int N>
inline
MoveState::Enum getMovedFrom(const AllocArgumentType<N>& object)
{
    return object.movedFrom();
}

template <int N>
inline
MoveState::Enum getMovedInto(const AllocArgumentType<N>& object)
{
    return object.movedInto();
}

}  // close package namespace

// TRAITS
namespace bslma {

template <int N>
struct UsesBslmaAllocator<bsltf::AllocArgumentType<N> > : bsl::true_type {};

}  // close namespace bslma

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