// bsltf_argumenttype.h                                               -*-C++-*-
#ifndef INCLUDED_BSLTF_ARGUMENTTYPE
#define INCLUDED_BSLTF_ARGUMENTTYPE

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

//@PURPOSE: Provide a non-allocating class to test variadic function arguments.
//
//@CLASSES:
//  bsltf::ArgumentType<N>: simple wrapper around an in-place 'int'
//
//@SEE_ALSO: bsltf_allocargumenttype, bsltf_templatetestfacility
//
//@DESCRIPTION: This component provides a class, 'bsltf::ArgumentType<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.  'bsltf::ArgumentType' does
// not allocate memory, and defines both copy and move constructors.
//
///Attributes
///----------
//..
//  Name                Type         Default
//  ------------------  -----------  -------
//  data                int          -1
//..
//: o 'data': representation of the class value
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Passing Arguments of the Correct Type and Order
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we wanted to test a function, 'forwardData', that takes a variable
// number of arguments and forwards them to another function (called
// 'delegateFunction', in this example).  Note, that the example below provides
// separate implementations for compilers that support C++11 standard and those
// that do not.  For clarity, we define 'forwardData' in line, and limit our
// expansion of the variadic template to 2 arguments on platforms that don't
// support variadic templates.
//
// First, we show the signature to the variadic function 'delegateFunction',
// that 'forwardData' (which we wish to test) will forward to (note that the
// implementation has been elided for simplicity):
//..
//  void delegateFunction();
//  void delegateFunction(ArgumentType<1> arg01);
//  void delegateFunction(ArgumentType<1> arg01, ArgumentType<2> arg02);
//..
// Then, we define the forwarding function we intend to test:
//..
//  #if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
//
//  template <class... Args>
//  inline
//  void forwardData(Args&&... arguments) {
//      delegateFunction(BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
//  }
//
//  #elif BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
//
//  inline
//  void forwardData()
//  {
//      delegateFunction();
//  }
//
//  template <class Args_01>
//  inline
//  void forwardData(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01)  arguments_01)
//  {
//      delegateFunction(BSLS_COMPILERFEATURES_FORWARD(Args_01, arguments_01));
//  }
//
//  template <class Args_01, class Args_02>
//  inline
//  void forwardData(BSLS_COMPILERFEATURES_FORWARD_REF(Args_01)  arguments_01,
//                   BSLS_COMPILERFEATURES_FORWARD_REF(Args_02)  arguments_02)
//  {
//      delegateFunction(BSLS_COMPILERFEATURES_FORWARD(Args_01, arguments_01),
//                       BSLS_COMPILERFEATURES_FORWARD(Args_02, arguments_02));
//  }
//
//  #else
//
//  // The code below is a workaround for the absence of perfect forwarding in
//  // some compilers.
//  template <class... Args>
//  inline
//  void forwardData(BSLS_COMPILERFEATURES_FORWARD_REF(Args)... arguments)
//  {
//      delegateFunction(BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
//  }
//
//  #endif
//..
// Finally, we define a test case for 'forwardData' passing 'ArgumentType' as
// variadic arguments to the 'forwardData' function and verifying that
// compilation succeeds:
//..
//  void usageExample()
//  {
//      forwardData();
//
//      ArgumentType<1> A01(1);
//      forwardData(A01);
//
//      ArgumentType<1> A11(13);
//      ArgumentType<2> A12(28);
//      forwardData(A11, A12);
//
//      // Note that passing arguments in a wrong order will fail to compile:
//      // ArgumentType<1> A21(3);
//      // ArgumentType<2> A22(82);
//      // forwardData(A22, A21);
//  }
//..

#include <bslscm_version.h>

#include <bsls_compilerfeatures.h>

#include <bsltf_movestate.h>

#include <bslmf_movableref.h>

#include <cstring>

namespace BloombergLP {
namespace bsltf {

                         // =======================
                         // class ArgumentType<int>
                         // =======================

template <int N>
class ArgumentType {
    // This class template declares a separate type for each template parameter
    // value 'N', 'bsltf::ArgumentType<N>', that wraps an integer value and
    // provides implicit conversion to and from 'int'.  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.

    // PRIVATE TYPES
    typedef bslmf::MovableRefUtil MoveUtil;

    // DATA
    int              d_data;       // attribute value
    MoveState::Enum  d_movedFrom;  // moved-from state
    MoveState::Enum  d_movedInto;  // moved-into state

  public:
    // CREATORS
    ArgumentType();
        // Create an 'ArgumentType' object having the default attribute value
        // '-1'.

    explicit ArgumentType(int value);
        // Create an 'ArgumentType' object having the specified 'value'. The
        // behavior is undefined unless 'value >= 0'.

    ArgumentType(const ArgumentType& original);
        // Create an 'ArgumentType' object having the same value as the
        // specified 'original'.

    ArgumentType(BloombergLP::bslmf::MovableRef<ArgumentType> original);
        // Create an 'ArgumentType' object having the same value as the
        // specified 'original'.  Note that 'original' is left in a valid but
        // unspecified state.

    ~ArgumentType();
        // Destroy this object.

    // MANIPULATORS
    ArgumentType& operator=(const ArgumentType& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    ArgumentType& operator=(BloombergLP::bslmf::MovableRef<ArgumentType> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.  Note
        // that 'rhs' is left in a valid but unspecified state.

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

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

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

};

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

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

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

                        // ---------------------
                        // class ArgumentType<N>
                        // ---------------------

// CREATORS
template <int N>
inline
ArgumentType<N>::ArgumentType()
: d_data(-1)
, d_movedFrom(MoveState::e_NOT_MOVED)
, d_movedInto(MoveState::e_NOT_MOVED)
{
}

template <int N>
inline
ArgumentType<N>::ArgumentType(int value)
: d_movedFrom(MoveState::e_NOT_MOVED)
, d_movedInto(MoveState::e_NOT_MOVED)
{
    BSLS_ASSERT_SAFE(value >= 0);

    d_data = value;
}

template <int N>
inline
ArgumentType<N>::ArgumentType(const ArgumentType& original)
: d_data(original.d_data)
, d_movedFrom(MoveState::e_NOT_MOVED)
, d_movedInto(MoveState::e_NOT_MOVED)
{
}

template <int N>
inline
ArgumentType<N>::ArgumentType(
                  BloombergLP::bslmf::MovableRef<ArgumentType> original)
: d_movedFrom(MoveState::e_NOT_MOVED)
, d_movedInto(MoveState::e_MOVED)
{
    ArgumentType& lvalue = original;

    d_data = lvalue.d_data;

    lvalue.d_data = -1;
    lvalue.d_movedFrom = MoveState::e_MOVED;
    lvalue.d_movedInto = MoveState::e_NOT_MOVED;
}

template <int N>
inline
ArgumentType<N>::~ArgumentType()
{
    const int garbage = 0xf0f0f0f0;

    d_data = ~d_data & garbage;
    std::memset(&d_movedFrom, 0xa5, sizeof(d_movedFrom));
    std::memset(&d_movedInto, 0xa5, sizeof(d_movedInto));
}

// MANIPULATORS
template <int N>
inline
ArgumentType<N>& ArgumentType<N>::operator=(const ArgumentType& rhs)
{
    if (this != &rhs) {
        d_data = rhs.d_data;
        d_movedFrom = MoveState::e_NOT_MOVED;
        d_movedInto = MoveState::e_NOT_MOVED;
    }
    return *this;
}

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

    if (this != &lvalue) {
        d_data = lvalue.d_data;
        d_movedFrom = MoveState::e_NOT_MOVED;
        d_movedInto = MoveState::e_MOVED;

        lvalue.d_data = -1;
        lvalue.d_movedFrom = MoveState::e_MOVED;
        lvalue.d_movedInto = MoveState::e_NOT_MOVED;
    }
    return *this;
}

// ACCESSORS
template <int N>
inline
ArgumentType<N>::operator int() const
{
    return d_data;
}

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

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

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

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

}  // close package namespace
}  // 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 ----------------------------------