// bsltf_stdallocatoradaptor.h                                        -*-C++-*-
#ifndef INCLUDED_BSLTF_STDALLOCATORADAPTOR
#define INCLUDED_BSLTF_STDALLOCATORADAPTOR

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

//@PURPOSE: Provide a mechanism to propagate standard allocator state.
//
//@CLASSES:
// bsltf::StdAllocatorAdaptor: adaptor that propagates allocator state
//
//@DESCRIPTION: This component provides an allocator adaptor class template,
// 'bsltf::StdAllocatorAdaptor', that mostly delegates operations to an
// allocator object of a (template parameter) allocator type, except that it
// enables the propagation of the (stateful) allocator object to constructed
// elements, if appropriate.  This class template enables reuse of test cases
// in higher level components (e.g., containers) written first using
// 'bslma::Allocator' and 'bslma::TestAllocator' to also test correct
// allocation using a C++ standard style allocator.
//
// StdAllocatorAdaptor' defines the minimal interface needed in order to comply
// with section 17.6.3.5 ([allocator.requirements]) of the C++11 standard. This
// class is similar to the 'scoped_allocator_adaptor' class template that is
// part of the C++11 standard, except that this adaptor does not support
// multiple levels of allocators (i.e., it is equivalent to the
// 'scoped_allocator_adaptor' with a single allocator).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Allocator Propagation
/// - - - - - - - - - - - - - - - -
// 'bslma::ConstructionUtil' propagates 'bslma::Allocator', wrapped by C++
// standard style allocator, to the constructor, if type, being constructed,
// supports 'UsesBslmaAllocator' trait.  'bsltf::StdAllocatorAdaptor' is used
// in test drivers to get the same behavior for the types that do not support
// that trait.
//
// Suppose, we want to adopt a test for a component that uses 'bslma'-style
// allocation to test that this component correctly works with standard
// allocators.  For simplicity the test below constructs an object of the
// (template parameter) type 'TYPE' by calling allocator's 'construct' method.
// We want to test that allocator is correctly propagated to the object
// constructor.  First, we define the test implementation:
//..
//  template<class TYPE, class ALLOC = bsl::allocator<TYPE> >
//  class TestDriver
//  {
//    public:
//      static void testCase()
//      {
//          bslma::TestAllocator oa("object");
//          ALLOC                xoa(&oa);
//
//          bsls::ObjectBuffer<TYPE> buffer;
//
//          xoa.construct(buffer.address(), 1);
//
//          bslma::DestructorGuard<TYPE> guard(&buffer.object());
//
//          const TYPE& X = buffer.object();
//
//          assert(1   == X.data());
//          assert(&oa == X.allocator());
//      }
//  };
//..
// Now, parameterize 'TestDriver' class with 'StdAllocatorAdaptor' explicitly
// to expand 'testCase' behavior for types, that don't support bslma
// allocators:
//..
//  template<class TYPE>
//  class StdBslmaTestDriver : public TestDriver<TYPE,
//                          bsltf::StdAllocatorAdaptor<bsl::allocator<TYPE> > >
//  {
//  };
//..
// Finally, run the test for types that use 'bslma' and standard allocators:
//..
//  TestDriver<AllocTestType>::testCase();
//  StdBslmaTestDriver<StdAllocTestType<bsl::allocator<int> > >::testCase();
//..

#include <bslscm_version.h>

#include <bslma_allocatortraits.h>

#include <bslmf_usesallocator.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#include <new>
#include <stddef.h>

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// Include version that can be compiled with C++03
// Generated on Thu Oct 21 10:11:37 2021
// Command line: sim_cpp11_features.pl bsltf_stdallocatoradaptor.h
# define COMPILING_BSLTF_STDALLOCATORADAPTOR_H
# include <bsltf_stdallocatoradaptor_cpp03.h>
# undef COMPILING_BSLTF_STDALLOCATORADAPTOR_H
#else

namespace BloombergLP {
namespace bsltf {

                        // =========================
                        // class StdAllocatorAdaptor
                        // =========================

template <class ALLOCATOR>
class StdAllocatorAdaptor : public ALLOCATOR {
    // This class template provides the facade of an allocator but mostly
    // delegates operations to the allocator object (of template parameter
    // type) it adapts, except that it enables the propagation of the
    // (stateful) allocator object to constructed elements, if appropriate.

    // PRIVATE TYPES
    typedef bsl::allocator_traits<ALLOCATOR> AllocatorTraits;

    // PRIVATE MANIPULATORS
#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14
    template <class ELEMENT_TYPE, class... Args>
    void privateConstruct(bsl::true_type,
                          ELEMENT_TYPE *elemAddr,
                          Args&&...     arguments);
        // Create an object of the (template parameter) 'ELEMENT_TYPE' at the
        // specified 'elemAddr', forwarding the allocator managed by this
        // adaptor and the specified (variable number of) 'arguments' to the
        // corresponding constructor of 'ELEMENT_TYPE'.

    template <class ELEMENT_TYPE, class... Args>
    void privateConstruct(bsl::false_type,
                          ELEMENT_TYPE *elemAddr,
                          Args&&...     arguments);
        // Create an object of the (template parameter) 'ELEMENT_TYPE' at the
        // specified 'elemAddr', forwarding the specified (variable number of)
        // 'arguments' to the corresponding constructor of 'ELEMENT_TYPE'.

#endif

  public:
    // PUBLIC TYPES
    typedef typename ALLOCATOR::size_type       size_type;
    typedef typename ALLOCATOR::difference_type difference_type;
    typedef typename ALLOCATOR::pointer         pointer;
    typedef typename ALLOCATOR::const_pointer   const_pointer;
    typedef typename ALLOCATOR::reference       reference;
    typedef typename ALLOCATOR::const_reference const_reference;
    typedef typename ALLOCATOR::value_type      value_type;

    template <class BDE_OTHER_TYPE>
    struct rebind
    {
        // This nested 'struct' template, parameterized by some
        // 'BDE_OTHER_TYPE', provides a namespace for an 'other' type alias,
        // which is an allocator type following the same template as this one
        // but that allocates elements of 'BDE_OTHER_TYPE'.  Note that this
        // allocator type is convertible to and from 'other' for any
        // 'BDE_OTHER_TYPE' including 'void'.

        typedef StdAllocatorAdaptor<
             typename ALLOCATOR::template rebind<BDE_OTHER_TYPE>::other> other;
    };

    // CREATORS
    StdAllocatorAdaptor();
        // Create a standard allocator adaptor object for a default-constructed
        // allocator object of the (template parameter) type 'ALLOCATOR'.

    StdAllocatorAdaptor(const ALLOCATOR& allocator);
        // Create a standard allocator adaptor object for the specified
        // 'allocator' of the (template parameter) type 'ALLOCATOR'.

    template <class ANY_TYPE>
    StdAllocatorAdaptor(const StdAllocatorAdaptor<ANY_TYPE>& other);
        // Create a copy of the specified 'other' allocator adaptor.

#ifdef BSLS_COMPILERFEATURES_SUPPORT_DEFAULTED_FUNCTIONS
    StdAllocatorAdaptor(const StdAllocatorAdaptor& original) = default;
        // Create a copy of the specified 'original' allocator adaptor.
#else
    //! StdAllocatorAdaptor(const StdAllocatorAdaptor& original) = default;
#endif

    //! ~StdAllocatorAdaptor() = default;
        // Destroy this object.

    // MANIPULATORS
#ifdef BSLS_COMPILERFEATURES_SUPPORT_DEFAULTED_FUNCTIONS
    StdAllocatorAdaptor& operator=(const StdAllocatorAdaptor& original) =
                                                                       default;
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.
#else
    //! StdAllocatorAdaptor& operator=(const StdAllocatorAdaptor& rhs) =
    //!                                                                default;
#endif

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14
    template <class ELEMENT_TYPE, class... Args>
    void construct(ELEMENT_TYPE *address, Args&&... arguments);
        // Construct an object of the (template parameter) 'ELEMENT_TYPE', by
        // forwarding the allocator instance associated with this allocator
        // adaptor, if appropriate, and the specified (variable number of)
        // 'arguments' to the corresponding constructor of 'ELEMENT_TYPE', at
        // the specified uninitialized memory 'address'.  The behavior is
        // undefined unless 'address' is properly aligned for objects of
        // 'ELEMENT_TYPE'.
#endif

    // ACCESSORS
    const ALLOCATOR& allocator() const;
        // Return a reference to the non-modifiable allocator instance
        // associated with this standard allocator adaptor.

    StdAllocatorAdaptor<ALLOCATOR>
                                 select_on_container_copy_construction() const;
        // Return an allocator adaptor for the allocator object returned by the
        // 'select_on_container_copy_construction' class method in the
        // 'allocator_traits' class template for the allocator object, of the
        // (template parameter) type 'ALLOCATOR', associated with this adaptor.
        // The 'allocator_traits' class template presumably delegates this call
        // to the allocator object if such an operation is supported by the
        // 'ALLOCATOR' type, or provides a suitable default behavior if such an
        // operation is not supported.
};

// FREE OPERATORS
template <class TYPE1, class TYPE2>
bool operator==(const StdAllocatorAdaptor<TYPE1>& lhs,
                const StdAllocatorAdaptor<TYPE2>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' adaptors are equal and
    // 'false' otherwise.  Two allocator adaptor instances are equal if their
    // associated allocator instances are equal.

template <class TYPE1, class TYPE2>
bool operator!=(const StdAllocatorAdaptor<TYPE1>& lhs,
                const StdAllocatorAdaptor<TYPE2>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' adaptors are not equal
    // and 'false' otherwise.  Two allocator adaptor instances are not equal if
    // their associated allocator instances are not equal.

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

                        // -------------------------
                        // class StdAllocatorAdaptor
                        // -------------------------

// PRIVATE MANIPULATORS
#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14
template <class ALLOCATOR>
template <class ELEMENT_TYPE, class... Args>
inline
void StdAllocatorAdaptor<ALLOCATOR>::privateConstruct(
                                                     bsl::true_type,
                                                     ELEMENT_TYPE   *address,
                                                     Args&&...       arguments)
{
    AllocatorTraits::construct(
        *this,
        address,
        BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...,
        *this);
}

template <class ALLOCATOR>
template <class ELEMENT_TYPE, class... Args>
inline
void StdAllocatorAdaptor<ALLOCATOR>::privateConstruct(
                                                    bsl::false_type,
                                                    ELEMENT_TYPE    *address,
                                                    Args&&...        arguments)
{
    AllocatorTraits::construct(
        *this, address, BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
}

#endif

// CREATORS
template <class ALLOCATOR>
inline
StdAllocatorAdaptor<ALLOCATOR>::StdAllocatorAdaptor()
: ALLOCATOR()
{
}

template <class ALLOCATOR>
inline
StdAllocatorAdaptor<ALLOCATOR>::StdAllocatorAdaptor(const ALLOCATOR& allocator)
: ALLOCATOR(allocator)
{
}

template <class ALLOCATOR>
template <class ANY_TYPE>
inline
StdAllocatorAdaptor<ALLOCATOR>::StdAllocatorAdaptor(
                                    const StdAllocatorAdaptor<ANY_TYPE>& other)
: ALLOCATOR(other)
{
}

// MANIPULATORS
#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14
template <class ALLOCATOR>
template <class ELEMENT_TYPE, class... Args>
inline void
StdAllocatorAdaptor<ALLOCATOR>::construct(ELEMENT_TYPE *address,
                                          Args&&...     arguments)
{
    privateConstruct(
        typename bsl::uses_allocator<
            ELEMENT_TYPE,
            StdAllocatorAdaptor<typename ALLOCATOR::template rebind<
                ELEMENT_TYPE>::other> >::type(),
        address,
        BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
}
#endif

// ACCESSORS
template <class ALLOCATOR>
inline
const ALLOCATOR& StdAllocatorAdaptor<ALLOCATOR>::allocator() const
{
    return *this;
}

template <class ALLOCATOR>
inline
StdAllocatorAdaptor<ALLOCATOR>
StdAllocatorAdaptor<ALLOCATOR>::select_on_container_copy_construction() const
{
    return StdAllocatorAdaptor<ALLOCATOR>(
        AllocatorTraits::select_on_container_copy_construction(
            *reinterpret_cast<const ALLOCATOR *>(this)));
}

// FREE OPERATORS
template <class TYPE1, class TYPE2>
inline
bool operator==(const bsltf::StdAllocatorAdaptor<TYPE1>& lhs,
                const bsltf::StdAllocatorAdaptor<TYPE2>& rhs)
{
    return lhs.allocator() == rhs.allocator();
}

template <class TYPE1, class TYPE2>
inline
bool operator!=(const bsltf::StdAllocatorAdaptor<TYPE1>& lhs,
                const bsltf::StdAllocatorAdaptor<TYPE2>& rhs)
{
    return lhs.allocator() != rhs.allocator();
}

}  // close package namespace
}  // close enterprise namespace

#endif // End C++11 code

#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 ----------------------------------