// bsltf_stdtestallocator.h                                           -*-C++-*-
#ifndef INCLUDED_BSLTF_STDTESTALLOCATOR
#define INCLUDED_BSLTF_STDTESTALLOCATOR

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

//@PURPOSE: Provide a minimal standard compliant allocator.
//
//@CLASSES:
//      bsltf::StdTestAllocatorConfiguration: namespace to configure allocator
// bsltf::StdTestAllocatorConfigurationGuard: configuration scoped guard
//                   bsltf::StdTestAllocator: standard compliant allocator
//
//@DESCRIPTION: This component provides an allocator, 'StdTestAllocator', that
// defines the minimal interface to comply with section 20.1.5
// ([lib.allocator.requirements]) of the C++03 standard.  This type can be used
// to verify that constructs designed to support a standard-compliant allocator
// access the allocator only through the standard-defined interface.
//
// 'StdTestAllocator' delegates its operations to a static 'bslma::Allocator'
// (delegate allocator) that can be configured by the utilities provided in the
// namespace 'StdTestAllocatorConfiguration'.
// 'StdTestAllocatorConfigurationGuard' provides a scoped guard to enable
// temporary replacement of the delegate allocator.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Testing The Support for STL-Compliant Allocator
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example we will verify that a type supports the use of a
// STL-compliant allocator.
//
// First we define a simple container type intended to be used with a C++03
// standard compliant allocator:
//..
//  template <class TYPE, class ALLOCATOR>
//  class MyContainer {
//      // This container type is parameterized on a standard allocator type
//      // and contains a single object, always initialized, which can be
//      // replaced and accessed.
//
//      // DATA MEMBERS
//      ALLOCATOR  d_allocator;  // allocator used to supply memory (held, not
//                               // owned)
//
//      TYPE      *d_object_p;   // pointer to the contained object
//
//    public:
//      // CONSTRUCTORS
//      MyContainer(const TYPE& object);
//          // Create an container containing the specified 'object', using the
//          // parameterized 'ALLOCATOR' to supply memory.
//
//      ~MyContainer();
//          // Destroy this container.
//
//      // MANIPULATORS
//      TYPE& object();
//          // Return a reference providing modifiable access to the object
//          // contained in this container.
//
//      // ACCESSORS
//      const TYPE& object() const;
//          // Return a reference providing non-modifiable access to the object
//          // contained in this container.
//  };
//..
// Then, we define the member functions of 'MyContainer':
//..
//  // CREATORS
//  template <class TYPE, class ALLOCATOR>
//  MyContainer<TYPE, ALLOCATOR>::MyContainer(const TYPE& object)
//  {
//      d_object_p = d_allocator.allocate(1);
//      d_allocator.construct(d_object_p, object);
//  }
//
//  template <class TYPE, class ALLOCATOR>
//  MyContainer<TYPE, ALLOCATOR>::~MyContainer()
//  {
//      d_allocator.destroy(d_object_p);
//      d_allocator.deallocate(d_object_p);
//  }
//
//  // MANIPULATORS
//  template <class TYPE, class ALLOCATOR>
//  TYPE& MyContainer<TYPE, ALLOCATOR>::object()
//  {
//      return *d_object_p;
//  }
//
//  // ACCESSORS
//  template <class TYPE, class ALLOCATOR>
//  const TYPE& MyContainer<TYPE, ALLOCATOR>::object() const
//  {
//      return *d_object_p;
//  }
//..
// Now, we use 'StdTestAllocator' to implement a simple test for 'MyContainer'
// to verify it correctly uses a parameterized allocator using only the C++03
// standard methods:
//..
//  bslma_TestAllocator oa("object", veryVeryVeryVerbose);
//  StdTestAllocatorConfigurationGuard stag(&oa);
//  {
//      typedef MyContainer<int, StdTestAllocator<int> > Obj;
//
//      Obj mX(2); const Obj& X = mX;
//      assert(sizeof(int) == oa.numBytesInUse());
//
//      assert(X.object() == 2);
//
//      mX.object() = -10;
//      assert(X.object() == -10);
//  }
//
//  assert(0 == oa.numBytesInUse());
//..

#include <bslscm_version.h>

#include <bslma_allocator.h>

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

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_types.h>
#include <bsls_util.h>

#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_stdtestallocator.h
# define COMPILING_BSLTF_STDTESTALLOCATOR_H
# include <bsltf_stdtestallocator_cpp03.h>
# undef COMPILING_BSLTF_STDTESTALLOCATOR_H
#else

namespace BloombergLP {
namespace bsltf {

                        // ===================================
                        // class StdTestAllocatorConfiguration
                        // ===================================

struct StdTestAllocatorConfiguration {
    // This 'struct' provides a namespace for functions that manipulate and
    // access the *delegate allocator* for 'StdTestAllocator'.  The delegate
    // allocator is the allocator to which 'StdTestAllocator' objects delegate
    // their operations.  The provided operations are *not* thread-safe.  Note
    // that this allocator is configured globally as C++03 standard compliant
    // allocators cannot have individually identifiable state.

  public:
    // CLASS METHODS
    static void setDelegateAllocatorRaw(bslma::Allocator *basicAllocator);
        // Set the address of the delegate allocator to the specified
        // 'basicAllocator'.

    static bslma::Allocator* delegateAllocator();
        // Return the address of the delegate allocator.  Note that, this
        // method will initially return
        // '&bslma_NewDeleteAllocator::singleton()' if the
        // 'setDelegatingAllocator' class method has not been called.
};

                        // ========================================
                        // class StdTestAllocatorConfigurationGuard
                        // ========================================

class StdTestAllocatorConfigurationGuard {
    // Upon construction, an object of this class saves the current *delegate
    // allocator* for 'StdTestAllocator' and and installs the user-specified
    // allocator as the delegate allocator.  The delegate allocator is the
    // globally configured allocator to which an 'StdTestAllocator' objects
    // delegate their operations.  On destruction, the original delegate
    // allocator is restored.

    bslma::Allocator *d_original_p;  // original (restore at destruction)

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

  public:
    // CREATORS
    explicit
    StdTestAllocatorConfigurationGuard(bslma::Allocator *temporaryAllocator);
        // Create a scoped guard that installs the specified
        // 'temporaryAllocator' as the delegate allocator.

    ~StdTestAllocatorConfigurationGuard();
        // Restore the delegate allocator that was in place when this scoped
        // guard was created and destroy this guard.
};


                        // ======================
                        // class StdTestAllocator
                        // ======================

template <class TYPE>
class StdTestAllocator {
    // This allocator implements the minimal interface to comply with section
    // 20.1.5 ([lib.allocator.requirements]) of the C++03 standard.  Instances
    // of this allocator delegate their operations to a globally configured
    // delegate allocator as C++03 compliant allocators cannot have
    // individually identifiable state (see 'StdTestAllocatorConfiguration' and
    // 'StdTestAllocatorConfigurationGuard).

  public:
    // PUBLIC TYPES
    // Deliberately use types that will *not* have the same representation as
    // the default 'size_t/ptrdiff_t' on most 64-bit platforms, yet will be
    // wide enough to support our regular testing, as verified on 32-bit
    // platforms.
    typedef bsls::Types::UintPtr  size_type;
    typedef bsls::Types::IntPtr   difference_type;
    typedef TYPE                 *pointer;
    typedef const TYPE           *const_pointer;
    typedef TYPE&                 reference;
    typedef const TYPE&           const_reference;
    typedef 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 StdTestAllocator<BDE_OTHER_TYPE> other;
    };

    // CREATORS
    StdTestAllocator();
        // Create a 'StdTestAllocator' object.

    // StdTestAllocator(const StdTestAllocator& original) = default;
        // Create a 'StdTestAllocator' object.  Note that this object will
        // compare equal to the default constructed object, because this type
        // has no state.

    template <class BDE_OTHER_TYPE>
    StdTestAllocator(const StdTestAllocator<BDE_OTHER_TYPE>&);
        // Create a 'StdTestAllocator' object.  Note that this object will
        // compare equal to the default constructed object, because this type
        // has no state.

    // ~StdTestAllocator() = default;
        // Destroy this object.

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

    pointer allocate(size_type numElements);
        // Allocate enough (properly aligned) space for the specified
        // 'numElements' of type 'T'.  The behavior is undefined unless
        // 'numElements <= max_size()'.

    void deallocate(pointer address, size_type numElements = 1);
        // Return memory previously at the specified 'address' for
        // 'numElements' back to this allocator.  The 'numElements' argument is
        // ignored by this allocator type.  The behavior is undefined unless
        // 'address' was allocated using this allocator object and has not
        // already been deallocated.

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14
    template <class ELEMENT_TYPE, class... Args>
    void construct(ELEMENT_TYPE *address, Args&&... arguments);
        // TBD: fix comment
        // Copy-construct a 'TYPE' object at the specified 'address'.  Do not
        // directly allocate memory.  The behavior is undefined unless
        // 'address' is properly aligned for 'TYPE'.
#endif

    template <class ELEMENT_TYPE>
    void destroy(ELEMENT_TYPE *address);
        // TBD: fix comment
        // Invoke the 'TYPE' destructor for the object at the specified
        // 'address'.

    // ACCESSORS
    pointer address(reference object) const;
        // Return the address providing modifiable access to 'object'.

    const_pointer address(const_reference object) const;
        // Return the address providing non-modifiable access to 'object'.

    size_type max_size() const;
        // Return the maximum number of elements of type 'TYPE' that can be
        // allocated using this allocator in a single call to the 'allocate'
        // method.  Note that there is no guarantee that attempts at allocating
        // less elements than the value returned by 'max_size' will not throw.
};

                          // ============================
                          // class StdTestAllocator<void>
                          // ============================

template <>
class StdTestAllocator<void> {
    // This specialization of 'StdTestAllocator' for 'void' type as the
    // parameterized 'TYPE' does not contain members that are unrepresentable
    // for 'void'.

  public:
    // PUBLIC TYPES

    // 'size_type' and 'difference_type' were deliberately changed from fixed
    // 32 bit types to being the size of a pointer, to avoid a cascade of
    // warnings on 64-bit builds.

    typedef bsls::Types::UintPtr  size_type;
    typedef bsls::Types::IntPtr   difference_type;
    typedef void                 *pointer;
    typedef const void           *const_pointer;
    typedef void                  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 StdTestAllocator<BDE_OTHER_TYPE> other;
    };

    // CREATORS
    StdTestAllocator();
        // Create a 'StdTestAllocator' object.

    // StdTestAllocator(const StdTestAllocator& original) = default;
        // Create a 'StdTestAllocator' object.  Note that this object will
        // compare equal to the default constructed object because, because
        // this type has no state.

    template <class BDE_OTHER_TYPE>
    StdTestAllocator(const StdTestAllocator<BDE_OTHER_TYPE>&);
        // Create a 'StdTestAllocator' object.  Note that this object will
        // compare equal to the default constructed object because, because
        // this type has no state.

    // ~StdTestAllocator() = default;
        // Destroy this object.

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

// FREE OPERATORS
template <class TYPE1, class TYPE2>
bool operator==(const StdTestAllocator<TYPE1>& lhs,
                const StdTestAllocator<TYPE2>& rhs);
    // Return 'true' because 'StdTestAllocator' does not hold a state.

template <class TYPE1, class TYPE2>
bool operator!=(const StdTestAllocator<TYPE1>& lhs,
                const StdTestAllocator<TYPE2>& rhs);
    // Return 'false' because 'StdTestAllocator' does not hold a state.


                        // ======================
                        // class StdTestAllocator
                        // ======================

struct StdTestAllocator_CommonUtil {
    // This 'struct' provides a namespace for utilities that are common to
    // all instantiations of the 'StdTestAllocator' class template.

    // CLASS METHODS
    static size_t maxSize(size_t elementSize);
        // Return the maximum number of objects, each taking the specified
        // 'elementSize' bytes of storage, that can potentially be allocated by
        // a 'StdTestAllocator'.  Note that this function is mostly about
        // insulating consumers of this component from a standard header, so
        // that this test component does not hide missing header dependencies
        // in testing scenarios.
};

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

                        // ----------------------------------------
                        // class StdTestAllocatorConfigurationGuard
                        // ----------------------------------------

// CREATORS
inline
StdTestAllocatorConfigurationGuard::StdTestAllocatorConfigurationGuard(
                                          bslma::Allocator *temporaryAllocator)
: d_original_p(StdTestAllocatorConfiguration::delegateAllocator())
{
    BSLS_ASSERT(temporaryAllocator);

    StdTestAllocatorConfiguration::setDelegateAllocatorRaw(temporaryAllocator);
}

inline
StdTestAllocatorConfigurationGuard::~StdTestAllocatorConfigurationGuard()
{
    BSLS_ASSERT(d_original_p);

    StdTestAllocatorConfiguration::setDelegateAllocatorRaw(d_original_p);
}

                        // ----------------------
                        // class StdTestAllocator
                        // ----------------------

// CREATORS
template <class TYPE>
inline
StdTestAllocator<TYPE>::StdTestAllocator()
{
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
StdTestAllocator<TYPE>::StdTestAllocator(
                                       const StdTestAllocator<BDE_OTHER_TYPE>&)
{
}

// MANIPULATORS
template <class TYPE>
inline
typename StdTestAllocator<TYPE>::pointer
StdTestAllocator<TYPE>::allocate(typename StdTestAllocator<TYPE>::size_type
                                                                   numElements)
{
    return
      static_cast<pointer>(StdTestAllocatorConfiguration::delegateAllocator()->
            allocate(bslma::Allocator::size_type(numElements * sizeof(TYPE))));
}

template <class TYPE>
inline
void StdTestAllocator<TYPE>::deallocate(pointer address, size_type)
{
    StdTestAllocatorConfiguration::delegateAllocator()->deallocate(address);
}

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14
template <class TYPE>
template <class ELEMENT_TYPE, class... Args>
inline void
StdTestAllocator<TYPE>::construct(ELEMENT_TYPE *address, Args&&... arguments)
{
    ::new (static_cast<void*>(address)) ELEMENT_TYPE(
                             BSLS_COMPILERFEATURES_FORWARD(Args,arguments)...);
}
#endif

template <class TYPE>
template <class ELEMENT_TYPE>
inline
void StdTestAllocator<TYPE>::destroy(ELEMENT_TYPE *address)
{
    address->~ELEMENT_TYPE();
}

template <class TYPE>
inline
typename StdTestAllocator<TYPE>::pointer
StdTestAllocator<TYPE>::address(reference object) const
{
    return bsls::Util::addressOf(object);
}

template <class TYPE>
inline
typename StdTestAllocator<TYPE>::const_pointer
StdTestAllocator<TYPE>::address(const_reference object) const
{
    return bsls::Util::addressOf(object);
}

template <class TYPE>
inline
typename StdTestAllocator<TYPE>::size_type
StdTestAllocator<TYPE>::max_size() const
{
    return StdTestAllocator_CommonUtil::maxSize(sizeof(TYPE));
}

                        // ----------------------------
                        // class StdTestAllocator<void>
                        // ----------------------------

// CREATORS
inline
StdTestAllocator<void>::StdTestAllocator()
{
}

template <class BDE_OTHER_TYPE>
StdTestAllocator<void>::StdTestAllocator(
                                       const StdTestAllocator<BDE_OTHER_TYPE>&)
{
}

// FREE OPERATORS
template <class TYPE1, class TYPE2>
inline
bool operator==(const bsltf::StdTestAllocator<TYPE1>&,
                const bsltf::StdTestAllocator<TYPE2>&)
{
    return true;
}

template <class TYPE1, class TYPE2>
inline
bool operator!=(const bsltf::StdTestAllocator<TYPE1>&,
                const bsltf::StdTestAllocator<TYPE2>&)
{
    return false;
}

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

#endif // End C++11 code

#endif

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