// bslalg_containerbase.h                                             -*-C++-*-
#ifndef INCLUDED_BSLALG_CONTAINERBASE
#define INCLUDED_BSLALG_CONTAINERBASE

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

//@PURPOSE: Provide a wrapper for STL allocators, respecting 'bslma' semantics.
//
//@CLASSES:
//  bslalg::ContainerBase: proxy class for STL-style containers
//
//@SEE_ALSO: bslma_stdallocator
//
//@DESCRIPTION: This component provides a single, mechanism class,
// 'bslalg::ContainerBase', that can used as a common base class for all
// STL-style containers.  A container should derive from this class to take
// advantage of empty-base optimization when a non-'bslma' allocator is used.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Creating a Fixed-Size Array with 'bslalg::ContainerBase'
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we would like to implement a fixed-size array that allocates memory
// on the heap at construction.
//
// First, we define the interface of the container, 'MyFixedSizeArray', that
// derives from 'ContainerBase'.  The implementation is elided for brevity:
//..
//  template <class VALUE, class ALLOCATOR>
//  class MyFixedSizeArray : private bslalg::ContainerBase<ALLOCATOR>
//      // This class implements a container that contains a fixed number of
//      // elements of the parameterized type 'VALUE' using the parameterized
//      // 'ALLOCATOR' to supply memory.  The number of elements is specified
//      // on construction.
//  {
//..
// Notice that to use this component, a class should derive from
// 'ContainerBase' in order to take advantage of empty-base optimization.
//..
//      // DATA
//      VALUE     *d_array;  // head pointer to the array of elements
//      const int  d_size;   // (fixed) number of elements in 'd_array'
//
//    public:
//      // CREATORS
//      MyFixedSizeArray(int size, const ALLOCATOR& allocator = ALLOCATOR());
//          // Create a 'MyFixedSizeArray' object having the specified 'size'
//          // elements, and using the specified 'allocator' to supply memory.
//
//      MyFixedSizeArray(const MyFixedSizeArray& original,
//                       const ALLOCATOR&        allocator = ALLOCATOR());
//          // Create a 'MyFixedSizeArray' object having same number of
//          // elements as that of the specified 'original', the same value of
//          // each element as that of corresponding element in 'original', and
//          // using the specified 'allocator' to supply memory.
//
//      ~MyFixedSizeArray();
//          // Destroy this object.
//
//      // MANIPULATORS
//      VALUE& operator[](int i);
//          // Return the reference of the specified 'i'th element of this
//          // object.  The behavior is undefined unless 'i < size()'.
//
//      // ACCESSORS
//      int size() const;
//          // Return the number of elements contained in this object.
//  };
//..
// Finally, assuming we have a STL compliant allocator named 'Allocator', we
// can create a 'MyFixedSizeArray' object and populate it with data.
//..
//  MyFixedSizeArray<int, Allocator<int> > fixedArray(3);
//  fixedArray[0] = 1;
//  fixedArray[1] = 2;
//  fixedArray[2] = 3;
//
//  assert(fixedArray[0] == 1);
//  assert(fixedArray[1] == 2);
//  assert(fixedArray[2] == 3);
//..

#include <bslscm_version.h>

#include <bslma_allocator.h>
#include <bslma_allocatortraits.h>

#include <bslmf_conditional.h>
#include <bslmf_isconvertible.h>

namespace BloombergLP {

namespace bslma { class Allocator; }

namespace bslalg {

                        // =============================
                        // class ContainerBase_BslmaBase
                        // =============================

template <class ALLOCATOR>
class ContainerBase_BslmaBase {
    // One of two possible base classes for 'ContainerBase'.  This class
    // should only be used for allocators that are based on 'bslma::Allocator'.
    // Provides access to the allocator.  Since 'ALLOCATOR' always has state
    // (at least a 'bslma::Allocator *'), there is no empty-base initialization
    // opportunity, so we don't inherit from 'ALLOCATOR' the way
    // 'ContainerBase_NonBslmaBase' does, below.  (Inheritance of this type can
    // cause ambiguous conversions and should be avoided or insulated.)

    ALLOCATOR d_allocator;

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

  public:
    // TYPES
    typedef ALLOCATOR AllocatorType;

    typedef bslma::Allocator *bslmaAllocatorPtr;
        // Pointer type returned by 'ALLOCATOR::mechanism'.  This type differs
        // in specializations of this class for non 'bslma'-based allocators.

    // CREATORS
    explicit
    ContainerBase_BslmaBase(const ALLOCATOR& basicAllocator);
        // Construct this object using the specified 'basicAllocator' of the
        // 'ALLOCATOR' parameterized type.

    ContainerBase_BslmaBase(const ContainerBase_BslmaBase& original);
        // Construct this object using the default allocator.  The 'original'
        // argument is ignored.  NOTE: This is obviously not a copy constructor
        // as is does not do any copying.  It does implement BSL-style
        // allocator semantics, whereby a newly created object must either have
        // an explicitly-specified allocator or else it uses the default
        // allocator object.  Under no circumstances is a BSL-style allocator
        // copied during copy construction or assignment.

    ~ContainerBase_BslmaBase();
        // Destroy this object.

    // MANIPULATORS
    ALLOCATOR& allocator();
        // Return a reference to the modifiable allocator used to construct
        // this object.

    // ACCESSORS
    const ALLOCATOR& allocator() const;
        // Return a reference to the non-modifiable allocator used to construct
        // this object.

    bslmaAllocatorPtr bslmaAllocator() const;
        // Return 'allocator().mechanism()'.
};

                        // ================================
                        // class ContainerBase_NonBslmaBase
                        // ================================

template <class ALLOCATOR>
class ContainerBase_NonBslmaBase : public ALLOCATOR {
    // One of two possible base classes for 'ContainerBase'.  This class is
    // for allocators that are not based on 'bslma::Allocator'.  Provides
    // access to the allocator.
    //
    // Because this class inherits from 'ALLOCATOR' it can take advantage of
    // empty-base optimization.  In other words, if 'ALLOCATOR' has no state
    // then it will not contribute to the footprint of the
    // 'ContainerBase_NonBslmaBase' object.  'ContainerBase_NonBslmaBase'
    // itself adds no state and will not increase the footprint of subsequently
    // derived classes.

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

  public:
    // TYPES
    typedef ALLOCATOR AllocatorType;

    typedef void *bslmaAllocatorPtr;
        // Generically, a pointer to the 'bslma::Allocator' mechanism type for
        // 'ALLOCATOR'.  Since the 'ALLOCATOR' type specified in this template
        // does not use the 'bslma::Allocator' mechanism, 'bslmaAllocatorPtr'
        // is simply 'void*'.  This can be used for overloading functions based
        // on whether a container uses a 'bslma'-based allocator or not.  (See
        // the 'bslma_constructionutil' and 'bslalg_arrayprimitives'
        // components.)

    // CREATORS
    ContainerBase_NonBslmaBase(const ALLOCATOR& basicAllocator);
        // Construct this object using the specified 'basicAllocator' or the
        // parameterized 'ALLOCATOR' type.

    ContainerBase_NonBslmaBase(const ContainerBase_NonBslmaBase& rhs);
        // Construct this object by copying the allocator from rhs.  NOTE:
        // Although this constructor does copy the allocator, the copy
        // constructor in the 'bslma'-specific 'ContainerBase_BslmaBase' class
        // (above) does not.

    ~ContainerBase_NonBslmaBase();
        // Destroy this object.

    // MANIPULATORS
    ALLOCATOR& allocator();
        // Return a reference to the modifiable allocator used to construct
        // this object.

    // ACCESSORS
    const ALLOCATOR& allocator() const;
        // Return a reference to the non-modifiable allocator used to construct
        // this object.

    bslmaAllocatorPtr bslmaAllocator() const;
        // Return a null pointer.  (This container's allocator does not use
        // 'bslma').  Note that the return type for this function differs from
        // the (typedef'ed) return type for the same function in the
        // 'ContainerBase_BslmaBase' class and can thus be used to choose one
        // overloaded function over another.
};

                        // ====================
                        // class ContainerBase
                        // ====================

template <class ALLOCATOR>
class ContainerBase : public
    bsl::conditional<bsl::is_convertible<bslma::Allocator*, ALLOCATOR>::value,
                     ContainerBase_BslmaBase<ALLOCATOR>,
                     ContainerBase_NonBslmaBase<ALLOCATOR> >::type {
    // Allocator proxy class for STL-style containers.  Provides access to the
    // allocator.  Implements the entire STL allocator interface, redirecting
    // allocation and deallocation calls to the proxied allocator.  One of two
    // possible base classes is chosen depending on whether 'ALLOCATOR' is
    // constructed from 'bslma::Allocator*'.

    // PRIVATE TYPES
    typedef typename
        bsl::conditional<
                  bsl::is_convertible<bslma::Allocator*, ALLOCATOR>::value,
                  ContainerBase_BslmaBase<ALLOCATOR>,
                  ContainerBase_NonBslmaBase<ALLOCATOR> >::type Base;

  public:
    // PUBLIC TYPES
    typedef typename Base::AllocatorType              AllocatorType;
    typedef bsl::allocator_traits<AllocatorType>      AllocatorTraits;

    typedef typename AllocatorTraits::size_type       size_type;
    typedef typename AllocatorTraits::difference_type difference_type;
    typedef typename AllocatorTraits::pointer         pointer;
    typedef typename AllocatorTraits::const_pointer   const_pointer;
    typedef typename AllocatorTraits::value_type      value_type;

        // Restate required allocator types.  (Reduces use of 'typename' in
        // interface.)

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

  private:
    // PRIVATE MANIPULATORS
    template <class T>
    typename AllocatorTraits::template rebind_traits<T>::allocator_type
    rebindAllocator(T*)
        // Return 'this->allocator()' rebound for type 'T'.  The 'T*' argument
        // is used only for template parameter deduction and is ignored.
    {
        // We use 'rebind_traits<T>::allocator_type' instead of the ideally
        // equivalent 'rebind_alloc<T>' because in C++03 it avoids the use of a
        // subclass of the allocator type that is needed to work around the
        // lack of alias templates.
        typedef typename AllocatorTraits::
            template rebind_traits<T>::allocator_type Rebound;
        return Rebound(this->allocator());
    }

  public:
    // CREATORS
    ContainerBase(const ALLOCATOR& basicAllocator);
        // Construct this object using the specified 'basicAllocator' of the
        // parameterized 'ALLOCATOR' type.

    ContainerBase(const ContainerBase& rhs);
        // Initialize this container base with rhs.  NOTE: This is not a true
        // copy constructor.  The allocator does not get copied if the
        // allocator is 'bslma'-based.  Using BSL allocator semantics, the
        // 'bslma'-style allocator must be supplied explicitly (i.e., not
        // copied from rhs) or else it is given a default value.  Non-'bslma'
        // allocators ARE copied because that is the way the ISO standard is
        // currently written.

    ~ContainerBase();
        // Destroy this object.

    // MANIPULATORS
    pointer allocate(size_type n, const void *hint = 0);
        // Allocate enough (properly aligned) space for 'n' objects of type 'T'
        // by calling 'allocate' on the mechanism object.  The 'hint' argument
        // is ignored by this allocator type.

    template <class T>
    T *allocateN(T* p, size_type n)
        // Allocate (but do not initialize) 'n' objects of type 'T' using the
        // allocator returned by 'allocator'.  Return a pointer to the raw
        // memory that was allocated.  The 'p' argument is used only to
        // determine the type of object being allocated; its value (usually
        // null) is not used.
    {
        return rebindAllocator(p).allocate(n);
    }

    void construct(pointer p, const value_type& val);
        // Copy-construct a 'T' object at the memory address specified by 'p'.
        // Do not directly allocate memory.  The behavior is undefined if 'p'
        // is not properly aligned for 'T'.

    void deallocate(pointer p, size_type n = 1);
        // Return memory previously allocated with 'allocate' to the underlying
        // mechanism object by calling 'deallocate' on the mechanism object.
        // The 'n' argument is ignored by this allocator type.

    template <class T>
    void deallocateN(T *p, size_type n)
        // Return 'n' objects of type 'T', starting at 'p' to the allocator
        // returned by 'allocator'.  Does not call destructors on the
        // deallocated objects.
    {
        rebindAllocator(p).deallocate(p, n);
    }

    void destroy(pointer p);
        // Call the 'T' destructor for the object pointed to by 'p'.  Do not
        // directly deallocate any memory.

    // ACCESSORS
    bool equalAllocator(const ContainerBase& rhs) const;
        // Returns 'this->allocator() == rhs.allocator()'.
};

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

                  // -----------------------------
                  // class ContainerBase_BslmaBase
                  // -----------------------------

// CREATORS
template <class ALLOCATOR>
inline
ContainerBase_BslmaBase<ALLOCATOR>::
ContainerBase_BslmaBase(const ALLOCATOR& basicAllocator)
: d_allocator(basicAllocator)
{
}

template <class ALLOCATOR>
inline
ContainerBase_BslmaBase<ALLOCATOR>::
ContainerBase_BslmaBase(const ContainerBase_BslmaBase&)
: d_allocator()
{
}

template <class ALLOCATOR>
inline
ContainerBase_BslmaBase<ALLOCATOR>::~ContainerBase_BslmaBase()
{
}

// MANIPULATORS
template <class ALLOCATOR>
inline
ALLOCATOR& ContainerBase_BslmaBase<ALLOCATOR>::allocator()
{
    return d_allocator;
}

// ACCESSORS
template <class ALLOCATOR>
inline
const ALLOCATOR& ContainerBase_BslmaBase<ALLOCATOR>::allocator() const
{
    return d_allocator;
}

template <class ALLOCATOR>
inline
typename ContainerBase_BslmaBase<ALLOCATOR>::bslmaAllocatorPtr
ContainerBase_BslmaBase<ALLOCATOR>::bslmaAllocator() const
{
    return d_allocator.mechanism();
}

                        // --------------------------------
                        // class ContainerBase_NonBslmaBase
                        // --------------------------------

// CREATORS
template <class ALLOCATOR>
inline
ContainerBase_NonBslmaBase<ALLOCATOR>::
ContainerBase_NonBslmaBase(const ALLOCATOR& basicAllocator)
: ALLOCATOR(basicAllocator)
{
}

template <class ALLOCATOR>
inline
ContainerBase_NonBslmaBase<ALLOCATOR>::
ContainerBase_NonBslmaBase(const ContainerBase_NonBslmaBase& rhs)
: ALLOCATOR(rhs.allocator())
{
}

template <class ALLOCATOR>
inline
ContainerBase_NonBslmaBase<ALLOCATOR>::~ContainerBase_NonBslmaBase()
{
}

// MANIPULATORS
template <class ALLOCATOR>
inline
ALLOCATOR& ContainerBase_NonBslmaBase<ALLOCATOR>::allocator()
{
    return *this;
}

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

template <class ALLOCATOR>
inline
typename ContainerBase_NonBslmaBase<ALLOCATOR>::bslmaAllocatorPtr
ContainerBase_NonBslmaBase<ALLOCATOR>::bslmaAllocator() const
{
    return 0;
}

                        // --------------------
                        // class ContainerBase
                        // --------------------

// CREATORS
template <class ALLOCATOR>
inline
ContainerBase<ALLOCATOR>::
ContainerBase(const ALLOCATOR& basicAllocator)
: Base(basicAllocator)
{
}

template <class ALLOCATOR>
inline
ContainerBase<ALLOCATOR>::
ContainerBase(const ContainerBase<ALLOCATOR>& rhs)
: Base(rhs)
{
}

template <class ALLOCATOR>
inline
ContainerBase<ALLOCATOR>::~ContainerBase()
{
}

// MANIPULATORS
template <class ALLOCATOR>
inline
typename ContainerBase<ALLOCATOR>::pointer
ContainerBase<ALLOCATOR>::allocate(size_type n, const void *hint)
{
    return this->allocator().allocate(n, hint);
}

template <class ALLOCATOR>
inline
void ContainerBase<ALLOCATOR>::deallocate(pointer p, size_type n)
{
    this->allocator().deallocate(p, n);
}

template <class ALLOCATOR>
inline
void ContainerBase<ALLOCATOR>::construct(pointer           p,
                                         const value_type& val)
{
    this->allocator().construct(p, val);
}

template <class ALLOCATOR>
inline
void ContainerBase<ALLOCATOR>::destroy(pointer p)
{
    this->allocator().destroy(p);
}

// ACCESSORS
template <class ALLOCATOR>
inline
bool ContainerBase<ALLOCATOR>::equalAllocator(const ContainerBase& rhs) const
{
    return this->allocator() == rhs.allocator();
}

}  // close package namespace

#ifndef BDE_OPENSOURCE_PUBLICATION  // BACKWARD_COMPATIBILITY
// ============================================================================
//                           BACKWARD COMPATIBILITY
// ============================================================================

#ifdef bslalg_ContainerBase
#undef bslalg_ContainerBase
#endif
#define bslalg_ContainerBase bslalg::ContainerBase
    // This alias is defined for backward compatibility.

#ifdef bslalg_ContainerBase_BslmaBase
#undef bslalg_ContainerBase_BslmaBase
#endif
#define bslalg_ContainerBase_BslmaBase bslalg::ContainerBase_BslmaBase
    // This alias is defined for backward compatibility.

#ifdef bslalg_ContainerBase_NonBslmaBase
#undef bslalg_ContainerBase_NonBslmaBase
#endif
#define bslalg_ContainerBase_NonBslmaBase bslalg::ContainerBase_NonBslmaBase
    // This alias is defined for backward compatibility.
#endif  // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY

}  // close enterprise namespace

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