// bslma_usesbslmaallocator.h                                         -*-C++-*-
#ifndef INCLUDED_BSLMA_USESBSLMAALLOCATOR
#define INCLUDED_BSLMA_USESBSLMAALLOCATOR

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

//@PURPOSE: Provide a metafunction to indicate the use of 'bslma' allocators.
//
//@CLASSES:
//  bslma::UsesBslmaAllocator: trait detection metafunction for 'bslma' use
//
//@SEE_ALSO: bslalg_typetraitusesblsmaallocator
//
//@DESCRIPTION: This component defines a metafunction,
// 'bslma::UsesBslmaAllocator', that may be used to associate a type with the
// uses-'bslma'-allocator trait (i.e., declare that a type uses a 'bslma'
// allocator), and also to detect whether a type has been associated with that
// trait (i.e., to test whether a type uses a 'bslma' allocator, and follows
// the 'bslma' allocator model).
//
///Properties of Types Declaring the 'UsesBslmaAllocator' Trait
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Types that declare the 'UsesBslmaAllocator' trait must meet some minimal
// requirements in order for that type to be usable with code that tests for
// the 'UsesBslmaAllocator' trait (e.g., 'bsl' containers).  In addition, types
// that use allocators must have certain properties with respect to memory
// allocation, which are not enforced by the compiler (such a type is described
// as following the 'bslma' allocator model).
//
///Compiler-Enforced Requirements of Types Declaring 'UsesBslmaAllocator'
///-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
// Types declaring the 'UsesBslmaAllocator' trait must provide a constructor
// variant that accepts a 'bslma::Allocator *' as the last argument (typically
// this is an optional argument).  If such a type provides a copy constructor,
// it must similarly provide a variant that takes a (optional)
// 'bslma::Allocator *' as the last argument.  If such a type provides a move
// constructor, it must similarly provide a variant that takes a
// 'bslma::Allocator *' as the last argument.
//
// Template types (such as 'bsl' containers), where the template parameter
// 'TYPE' represents some element type encapsulated by the class template,
// often use the 'UsesBslmaAllocator' trait to test if 'TYPE' uses 'bslma'
// allocators and, if so, to create 'TYPE' objects using the constructor
// variant taking an allocator.  In this context, compilation will fail if a
// type declares the 'UsesBslmaAllocator' trait, but does not provide the
// expected constructor variant accepting a 'bslma::Allocator *' as the last
// argument.
//
///Expected Properties of Types Declaring the 'UsesBslmaAllocator' Trait
///  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
// Types declaring the 'UsesBslmaAllocator' trait are expected to have certain
// properties with respect to memory allocation.  These properties are not
// enforced by the compiler, but are necessary to ensure consistent and
// comprehensible allocation behavior.
//
//: o The allocator supplied at construction of an object will be used for
//:   non-transient memory allocation during the object's lifetime.  This
//:   particularly includes allocations performed by sub-objects that
//:   themselves support the 'bslma' allocator model (i.e., the type will
//:   provide the supplied allocator to any data members that themselves accept
//:   an allocator).
//:
//: o If the type defines a move constructor *with* an allocator parameter:
//:   1 If another move constructor *without* an allocator parameter that is
//:     'noexcept' exists, then if the allocators match, the behavior of those
//:     two move constructors will be identical.
//:   2 If the type defines a copy constructor, then the behavior of this
//:     move constructor when allocators don't match will be the same as the
//:     behavior of the copy constructor.
//:
//: o The allocator used by an object is not changed after construction (e.g.,
//:   the assignment operator does not change the allocator used by an object).
//:
//: o Transient memory allocations -- i.e., allocations performed within the
//:   scope of a function where the resulting memory is deallocated before
//:   that function returns -- are generally *not* performed using the object's
//:   allocator.  Although clients may choose whichever allocator suits the
//:   specific context, most often transient memory allocations are performed
//:   using the currently installed default allocator.  For example, a
//:   temporary 'bsl::string' that is destroyed within the scope of a method
//:   need not be explicitly supplied an allocator, since it is a transient
//:   allocation, and 'bsl::string' will use the default allocator by default.
//:
//: o The allocator used by an object is not part of an object's value (e.g.,
//:   it is not tested by the equality-comparison operator 'operator==').
//:
//: o If an allocator is not supplied at construction, then the currently
//:   installed default allocator will typically be used (see 'bslma_default').
//:
//: o If a move constructor is called with no allocator argument, the allocator
//:   used by the new object will be the same as the allocator used by the
//:   source object.
//:
//: o Singleton objects, when necessary, allocate memory from the global
//:   allocator (see 'bslma_default').
//
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example 1: Associating the 'bslma' Allocator Trait with a Type
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we want to declare two types that make use of a 'bslma' allocator,
// and need to associate the 'UsesBslmaAllocator' trait with those types (so
// that they behave correctly when inserted into a 'bsl' container, for
// example).  In this example we will demonstrate two different mechanisms by
// which a trait may be associated with a type.
//
// First, we define a type 'UsesAllocatorType1' and use the
// 'BSLMF_NESTED_TRAIT_DECLARATION' macro to associate the type with the
// 'UsesBslmaAllocator' trait:
//..
//  namespace xyz {
//
//  class UsesAllocatorType1 {
//      // ...
//
//    public:
//      // TRAITS
//      BSLMF_NESTED_TRAIT_DECLARATION(UsesAllocatorType1,
//                                     bslma::UsesBslmaAllocator);
//      // CREATORS
//      explicit UsesAllocatorType1(bslma::Allocator *basicAllocator = 0);
//          // ...
//
//      UsesAllocatorType1(const UsesAllocatorType1&  original,
//                         bslma::Allocator          *basicAllocator = 0);
//          // ...
//  };
//..
// Notice that the macro declaration is performed within the scope of the class
// declaration, and must be done with 'public' access.
//
// Then, we define a type 'UsesAllocatorType2' and define a specialization of
// the 'UsesBslmaAllocator' trait for 'UsesAllocatorType2' that associates the
// 'UsesBslmaAllocator' trait with the type (note that this is sometimes
// referred to as a "C++11-style" trait declaration, since it is more in
// keeping with the style of trait declarations found in the C++11 Standard):
//..
//  class UsesAllocatorType2 {
//      // ...
//
//    public:
//      // CREATORS
//      explicit UsesAllocatorType2(bslma::Allocator *basicAllocator = 0);
//          // ...
//
//      UsesAllocatorType2(const UsesAllocatorType2&  original,
//                         bslma::Allocator          *basicAllocator = 0);
//          // ...
//  };
//
//  }  // close package namespace
//
//  // TRAITS
//  namespace BloombergLP {
//  namespace bslma {
//
//  template <> struct UsesBslmaAllocator<xyz::UsesAllocatorType2> :
//                                                               bsl::true_type
//  {};
//
//  }  // close namespace bslma
//  }  // close enterprise namespace
//..
// Notice that the specialization must be performed in the 'BloombergLP::bslma'
// namespace.
//
///Example 2: Testing Whether a Type Uses a 'bslma' Allocator
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we want to test whether each of a set of different types uses a
// 'bslma' allocator.
//
// Here, we use the 'UsesBslmaAllocator' template to test whether the types
// 'DoesNotUseAnAllocatorType', 'UsesAllocatorType1', and 'UsesAllocatorType2'
// (see above for the latter two types) use allocators:
//..
//  class DoesNotUseAnAllocatorType {
//  };
//
//  assert(false ==
//         bslma::UsesBslmaAllocator<DoesNotUseAnAllocatorType>::value);
//
//  assert(true  ==
//         bslma::UsesBslmaAllocator<xyz::UsesAllocatorType1>::value);
//
//  assert(true  ==
//         bslma::UsesBslmaAllocator<xyz::UsesAllocatorType2>::value);
//..
// Finally, we demonstrate that the trait can be tested at compilation time by
// testing the trait within the context of a compile-time 'BSLMF_ASSERT':
//..
//  BSLMF_ASSERT(false ==
//               bslma::UsesBslmaAllocator<DoesNotUseAnAllocatorType>::value);
//
//  BSLMF_ASSERT(true  ==
//               bslma::UsesBslmaAllocator<xyz::UsesAllocatorType1>::value);
//
//  BSLMF_ASSERT(true ==
//               bslma::UsesBslmaAllocator<xyz::UsesAllocatorType2>::value);
//..

#include <bslscm_version.h>

#include <bslma_allocator.h>

#include <bslmf_detectnestedtrait.h>
#include <bslmf_integralconstant.h>
#include <bslmf_isconvertible.h>

namespace BloombergLP {
namespace bslma {

                        // ========================
                        // class UsesBslmaAllocator
                        // ========================

template <class TYPE, bool IS_NESTED>
struct UsesBslmaAllocator_Imp
{
    typedef bsl::integral_constant<bool, IS_NESTED> Type;
};

template <class TYPE>
struct UsesBslmaAllocator_Imp<TYPE, false>
{
  private:
    struct UniqueType {
        // A class convertible from this type must have a constructor template
        // callable with a single argument of any (pointer) type, or a
        // constructor with a single 'void *' parameter (or all subsequent
        // parameters have a default argument), which makes it convertible from
        // EVERY pointer type.
    };

    enum {
        // If a pointer to 'Allocator' is convertible to 'T', then 'T' has a
        // non-explicit constructor taking an allocator.
        k_BSLMA_POINTER_CTOR = bsl::is_convertible<Allocator *, TYPE>::value,

        // If a pointer to 'UniqueType' is convertible to 'T', it can only mean
        // that ANY POINTER is convertible to 'T'.
        k_ANY_POINTER_CTOR = bsl::is_convertible<UniqueType *, TYPE>::value
    };

  public:
    typedef bsl::integral_constant<bool,
                                   k_BSLMA_POINTER_CTOR
                                   && !k_ANY_POINTER_CTOR>
        Type;
};

template <class TYPE>
struct UsesBslmaAllocator
    : UsesBslmaAllocator_Imp<
        TYPE,
        bslmf::DetectNestedTrait<TYPE, UsesBslmaAllocator>::value>::Type::type
{
    // This metafunction is derived from 'true_type' if 'TYPE' adheres to the
    // 'bslma' allocator usage idiom and 'false_type' otherwise.  Note that
    // this trait must be explicitly associated with a type in order for this
    // metafunction to return true; simply having a constructor that implicitly
    // converts 'bslma::Allocator *' to 'TYPE' is no longer sufficient for
    // considering a type follow the idiom.
};

template <class TYPE>
struct UsesBslmaAllocator<TYPE *> : bsl::false_type
{
    // Specialization that avoids special-case template metaprogramming to
    // handle 'bslma::Allocator *' being convertible to itself, but not being
    // a type that uses allocators.  This is true for all pointers, so we take
    // advantage of the simpler metaprogram in all such cases.
};

template <class TYPE>
struct UsesBslmaAllocator<TYPE&> : bsl::false_type
{
    // Specialization that avoids special-case template metaprogramming to
    // handle 'bslma::Allocator *' being convertible to a 'const &' via a
    // temporary object.
};

template <class TYPE>
struct UsesBslmaAllocator<const TYPE> : UsesBslmaAllocator<TYPE>::type
{
    // Specialization that associates the same trait with 'const TYPE' as with
    // unqualified 'TYPE'.
};

template <class TYPE>
struct UsesBslmaAllocator<volatile TYPE> : UsesBslmaAllocator<TYPE>::type
{
    // Specialization that associates the same trait with 'volatile TYPE' as
    // with unqualified 'TYPE'.
};

template <class TYPE>
struct UsesBslmaAllocator<const volatile TYPE> : UsesBslmaAllocator<TYPE>::type
{
    // Specialization that associates the same trait with 'const volatile
    // TYPE' as with unqualified 'TYPE'.
};

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