// bslmf_selecttrait.h                                                -*-C++-*-
#ifndef INCLUDED_BSLMF_SELECTTRAIT
#define INCLUDED_BSLMF_SELECTTRAIT

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

//@PURPOSE: Provide clean compile-time dispatch based on multiple traits
//
//@CLASSES:
// bslmf::SelectTrait        Template that selects the first of multiple traits
// bslmf::SelectTraitCase    Template that identifies a selected trait
//
//@SEE_ALSO:
//
//@DESCRIPTION:
//
///Usage
///-----
// This section illustrates the intended usage of this component.
//
///Example 1: Dispatch On Traits
///- - - - - - - - - - - - - - -
// We would like to create a function template,
// 'ScalarPrimitives::copyConstruct', that takes an original object and an
// allocator constructs a copy of 'original' using the most efficient valid
// mechanism.  The function should take into account that the original type
// might be bitwise copyable, or have an allocator that can be different in
// the copy than in the original object, or that the original might be a pair
// type, where the correct method of copying 'first' and 'second' is
// (recursively) governed by the same concerns.
//
// The old (legacy) 'bsls::HasTrait' mechanism has a clumsy mechanism for
// dispatching on multiple traits at once.  For example, the
// 'bslalg::scalarprimitives::copyConstruct', function uses four different
// implementations, depending on the traits of the object being copied.  The
// existing code looks like this:
//..
//  template <class TARGET_TYPE>
//  inline
//  void
//  ScalarPrimitives::copyConstruct(TARGET_TYPE        *address,
//                                  const TARGET_TYPE&  original,
//                                  bslma::Allocator   *allocator)
//  {
//      BSLS_ASSERT_SAFE(address);
//
//      enum {
//          VALUE = HasTrait<TARGET_TYPE,
//                                  TypeTraitUsesBslmaAllocator
//                                 >::VALUE ? Imp::USES_BSLMA_ALLOCATOR_TRAITS
//                : HasTrait<TARGET_TYPE,
//                                  TypeTraitBitwiseCopyable
//                                 >::VALUE ? Imp::BITWISE_COPYABLE_TRAITS
//                : HasTrait<TARGET_TYPE,
//                                  TypeTraitPair
//                                 >::VALUE ? Imp::PAIR_TRAITS
//                : Imp::NIL_TRAITS
//      };
//      Imp::copyConstruct(address, original, allocator,
//                         (bsl::integral_constant<int, VALUE>*)0);
//  }
//..
// We would like to replace the cumbersome chain of '?:' operations with a
// clean mechanism for producing one of four different types based on the
// first matching trait.
//
// First, we create three traits metafunctions to replace the three legacy
// traits used above:
//..
//  template <class t_TYPE> struct UsesBslmaAllocator : bsl::false_type { };
//  template <class t_TYPE> struct IsBitwiseCopyable : bsl::false_type { };
//  template <class t_TYPE> struct IsPair : bsl::false_type { };
//..
// Note that these definitions are simplified to avoid excess dependencies; A
// proper traits definition would inherit from 'bslmf::DetectNestedTrait'
// instead of from 'bsl::false_type'.
//
// Next, we forward-declare 'bslma::Allocator' and
// 'bslalg::scalarprimitives::copyConstruct':
//..
//  namespace bslma { class Allocator; }
//
//  namespace bslalg {
//  struct ScalarPrimitives {
//      template <class TARGET_TYPE>
//      static void copyConstruct(TARGET_TYPE        *address,
//                                const TARGET_TYPE&  original,
//                                bslma::Allocator   *allocator);
//  };
//..
// Next, we implement three overloads of 'Imp::copyConstruct', each taking a
// different trait specialization.  A fourth overload takes 'false_type'
// instead of a trait specialization, for those types that don't match any
// traits.  For testing purposes, in addition to copying the data member, each
// overload also increments a separate counter.  These implementations are
// slightly simplified for readability:
//..
//  struct Imp {
//
//      // Counters for counting overload calls
//      static int s_noTraitsCounter;
//      static int s_usesBslmaAllocatorCounter;
//      static int s_isPairCounter;
//      static int s_isBitwiseCopyableCounter;
//
//      static void clearCounters() {
//          s_noTraitsCounter = 0;
//          s_usesBslmaAllocatorCounter = 0;
//          s_isPairCounter = 0;
//          s_isBitwiseCopyableCounter = 0;
//      }
//
//      template <class TARGET_TYPE>
//      static void
//      copyConstruct(TARGET_TYPE                                 *address,
//                    const TARGET_TYPE&                           original,
//                    bslma::Allocator                            *allocator,
//                    bslmf::SelectTraitCase<UsesBslmaAllocator>)
//      {
//          new (address) TARGET_TYPE(original, allocator);
//          ++s_usesBslmaAllocatorCounter;
//      }
//
//      template <class TARGET_TYPE>
//      static void
//      copyConstruct(TARGET_TYPE                 *address,
//                    const TARGET_TYPE&           original,
//                    bslma::Allocator            *allocator,
//                    bslmf::SelectTraitCase<IsPair>)
//      {
//          ScalarPrimitives::copyConstruct(&address->first, original.first,
//                                          allocator);
//          ScalarPrimitives::copyConstruct(&address->second, original.second,
//                                          allocator);
//          ++s_isPairCounter;
//      }
//
//      template <class TARGET_TYPE>
//      static void
//      copyConstruct(TARGET_TYPE                             *address,
//                    const TARGET_TYPE&                       original,
//                    bslma::Allocator                        *,
//                    bslmf::SelectTraitCase<IsBitwiseCopyable>)
//      {
//          memcpy(address, &original, sizeof(original));
//          ++s_isBitwiseCopyableCounter;
//      }
//
//      template <class TARGET_TYPE>
//      static void
//      copyConstruct(TARGET_TYPE                *address,
//                    const TARGET_TYPE&          original,
//                    bslma::Allocator           *,
//                    bslmf::SelectTraitCase<>)
//      {
//          new (address) TARGET_TYPE(original);
//          ++s_noTraitsCounter;
//      }
//  };
//
//  int Imp::s_noTraitsCounter = 0;
//  int Imp::s_usesBslmaAllocatorCounter = 0;
//  int Imp::s_isPairCounter = 0;
//  int Imp::s_isBitwiseCopyableCounter = 0;
//..
// Then, we implement 'ScalarPrimitives::copyConstruct':
//..
//  template <class TARGET_TYPE>
//  inline void
//  ScalarPrimitives::copyConstruct(TARGET_TYPE        *address,
//                                  const TARGET_TYPE&  original,
//                                  bslma::Allocator   *allocator)
//  {
//..
// We use 'bslmf::SelectTrait' to declare 'Selection' as a specialization
// of the first match of the specified traits:
//..
//      typedef typename bslmf::SelectTrait<TARGET_TYPE,
//                                          UsesBslmaAllocator,
//                                          IsBitwiseCopyable,
//                                          IsPair>::Type Selection;
//..
// Now, we use 'Selection' to choose (at compile time), one of the
// 'Imp::copyConstruct' overloads defined above:
//..
//      Imp::copyConstruct(address, original, allocator, Selection());
//  } // end copyConstruct()
//
//  } // Close namespace bslalg
//..
// Finally, we define three classes, associated with each of the three traits
// of interest, a fourth class associated with more than one trait (to show
// that the selection mechanism respects preference) and a fifth class that is
// not associated with any trait.
//
// The first class is associated with the 'UsesBslmaAllocator' trait:
//..
//  class TypeWithAllocator {
//      int               d_value;
//      bslma::Allocator *d_alloc;
//  public:
//      TypeWithAllocator(int v = 0, bslma::Allocator *a = 0)       // IMPLICIT
//          : d_value(v), d_alloc(a) { }
//      TypeWithAllocator(const TypeWithAllocator& other,
//                        bslma::Allocator *a = 0)
//          : d_value(other.d_value), d_alloc(a) {  }
//
//      int value() const { return d_value; }
//      bslma::Allocator *allocator() const { return d_alloc; }
//  };
//
//  template <> struct UsesBslmaAllocator<TypeWithAllocator>
//      : bsl::true_type { };
//..
// The second class is associated with the 'IsBitwiseCopyable' trait:
//..
//  class BitwiseCopyableType {
//      int d_value;
//  public:
//      BitwiseCopyableType(int v = 0) : d_value(v) { }             // IMPLICIT
//      int value() const { return d_value; }
//  };
//
//  template <> struct IsBitwiseCopyable<BitwiseCopyableType>
//      : bsl::true_type { };
//..
// The third class is associated with the 'IsPair' trait:
//..
//  struct PairType {
//      TypeWithAllocator   first;
//      BitwiseCopyableType second;
//
//      PairType(int a, int b) : first(a), second(b) { }
//  };
//
//  template <> struct IsPair<PairType> : bsl::true_type { };
//..
// The fourth class is associated with both the 'IsPair' and
// 'IsBitwiseCopyable' traits:
//..
//  struct BitwiseCopyablePairType {
//      BitwiseCopyableType first;
//      BitwiseCopyableType second;
//
//      BitwiseCopyablePairType(int a, int b) : first(a), second(b) { }
//  };
//
//  template <> struct IsPair<BitwiseCopyablePairType> : bsl::true_type { };
//  template <> struct IsBitwiseCopyable<BitwiseCopyablePairType>
//      : bsl::true_type { };
//..
// The fifth class is not associated with any explicit traits:
//..
//  class TypeWithNoTraits {
//      int d_value;
//  public:
//      TypeWithNoTraits(int v = 0) : d_value(v) { }                // IMPLICIT
//      int value() const { return d_value; }
//  };
//..
// We use these classes to instantiate 'ScalarPrimitives::copyConstruct' and
// verify that the most efficient copy operation that is valid for each type
// is applied:
//..
//  int usageExample1()
//  {
//      using bslalg::Imp;
//
//      // This buffer is properly aligned and big enough to hold any of the
//      // test types.
//      void *buffer[4];
//      char dummy[2];  // Dummy addresses
//
//      bslma::Allocator *a1 = (bslma::Allocator*) &dummy[0];
//      bslma::Allocator *a2 = (bslma::Allocator*) &dummy[1];
//..
// When we call 'ScalarPrimitives::copyConstruct' for an object of
// 'TypeWithAllocator', we expect that the copy will have the same value but a
// different allocator than the original and that the
// 'UsesBslmaAllocator' copy implementation will be called once:
//..
//      Imp::clearCounters();
//      TypeWithAllocator  twa(1, a1);
//      TypeWithAllocator *twaptr = (TypeWithAllocator*) buffer;
//      bslalg::ScalarPrimitives::copyConstruct(twaptr, twa, a2);
//      assert(1 == Imp::s_usesBslmaAllocatorCounter);
//      assert(1 == twaptr->value());
//      assert(a2 == twaptr->allocator());
//      twaptr->~TypeWithAllocator();
//..
// When we call 'ScalarPrimitives::copyConstruct' for an object of
// 'BitwiseCopyableType', we expect that the 'IsBitwiseCopyable' copy
// implementation will be called once:
//..
//      Imp::clearCounters();
//      BitwiseCopyableType  bct(2);
//      BitwiseCopyableType *bctptr = (BitwiseCopyableType*) buffer;
//      bslalg::ScalarPrimitives::copyConstruct(bctptr, bct, a2);
//      assert(1 == Imp::s_isBitwiseCopyableCounter);
//      assert(2 == bctptr->value());
//      bctptr->~BitwiseCopyableType();
//..
// When we call 'ScalarPrimitives::copyConstruct' for an object of
// 'PairType', we expect that the 'IsPair' copy implementation will be
// called once for the pair as whole and that the
// 'UsesBslmaAllocator' and 'IsBitwiseCopyable' implementations
// will be called for the 'first' and 'second' members, respectively:
//..
//      Imp::clearCounters();
//      PairType  pt(3, 4);
//      PairType *ptptr = (PairType*) buffer;
//      bslalg::ScalarPrimitives::copyConstruct(ptptr, pt, a2);
//      assert(1 == Imp::s_isPairCounter);
//      assert(1 == Imp::s_usesBslmaAllocatorCounter);
//      assert(1 == Imp::s_usesBslmaAllocatorCounter);
//      assert(3 == ptptr->first.value());
//      assert(a2 == ptptr->first.allocator());
//      assert(4 == ptptr->second.value());
//      ptptr->~PairType();
//..
// When we call 'ScalarPrimitives::copyConstruct' for an object of
// 'BitwiseCopyablePairType', the 'IsBitwiseCopyable' trait takes precedence
// over the 'IsPair' trait (because it appears first in the list of traits
// used to instantiate 'SelectTrait').  Therefore, we expect to see the
// 'IsBitwiseCopyable' copy implementation called once for the whole
// pair and the 'IsPair' copy implementation not called at all:
//..
//      Imp::clearCounters();
//      BitwiseCopyablePairType  bcpt(5, 6);
//      BitwiseCopyablePairType *bcptbcptr = (BitwiseCopyablePairType*) buffer;
//      bslalg::ScalarPrimitives::copyConstruct(bcptbcptr, bcpt, a2);
//      // Prefer IsBitwiseCopyable over IsPair trait
//      assert(1 == Imp::s_isBitwiseCopyableCounter);
//      assert(0 == Imp::s_isPairCounter);
//      assert(5 == bcptbcptr->first.value());
//      assert(6 == bcptbcptr->second.value());
//      bcptbcptr->~BitwiseCopyablePairType();
//..
// When we call 'ScalarPrimitives::copyConstruct' for an object of
// 'TypeWithNoTraits', we expect none of the specialized copy implementations
// to be called, thus defaulting to the 'false_type' copy implementation:
//..
//      Imp::clearCounters();
//      TypeWithNoTraits  twnt(7);
//      TypeWithNoTraits *twntptr = (TypeWithNoTraits*) buffer;
//      bslalg::ScalarPrimitives::copyConstruct(twntptr, twnt, a2);
//      assert(1 == Imp::s_noTraitsCounter);
//      assert(7 == twntptr->value());
//      twntptr->~TypeWithNoTraits();
//
//      return 0;
//  }
//..
// Note that using 'SelectTraits' for dispatching using overloading imposes
// little or no overhead, since the compiler typically generates no code for
// the constructor or copy constructor of the trait argument to
// the overloaded functions.  When inlining is in effect, the result is very
// efficient.

#include <bslscm_version.h>

#include <bslmf_integralconstant.h>
#include <bslmf_switch.h>

namespace BloombergLP {

namespace bslmf {

                        // ========================
                        // struct SelectTrait_False
                        // ========================

template <class>
struct SelectTrait_False : bsl::false_type
{
    // Metafunction that always returns false.
};

                           // ======================
                           // struct SelectTraitCase
                           // ======================

template <template <class> class t_TRAIT = SelectTrait_False>
struct SelectTraitCase
{
    // This template expresses a class that is unique for the specified
    // (template parameter) 't_TRAIT' metafunction.  An instantiation of this
    // template is the "compile-time return value" of 'SelectTrait' (see
    // below).  'SelectTraitCase' acts as a sort of compile-time
    // pointer-to-metafunction that holds the identity of a metafunction
    // similar to the way a pointer-to-function holds (at run-time) the
    // identity of a function.  As in the pointer-to-function case, a
    // 'SelectTraitCase' can also be used indirectly to evaluate 't_TRAIT' (at
    // compile time).  Also note that, when 'SelectTraitCase' is specialized
    // with the default 't_TRAIT' type parameter, 'SelectTrait_False', it
    // essentially means that none of the traits specified to 'SelectTrait'
    // match.

    template <class t_TYPE>
    struct Eval : public t_TRAIT<t_TYPE>::type {
        // Evaluates 't_TRAIT' for the specified (template parameter) 'T' type.
        // The resulting 'Eval<T>' instantiation is derived from 'true_type' if
        // 't_TRAIT<T>' is derived from 'true_type' and 'false_type' if
        // 't_TRAIT<T>' is derived from 'false_type'.  (More generally,
        // 'Eval<T>' is derived from 't_TRAIT<T>::type'.)
    };

    typedef SelectTraitCase Type;
};

                        // ======================
                        // struct SelectTrait_Imp
                        // ======================

template <class t_TYPE,
          template <class> class t_TRAIT1,
          template <class> class t_TRAIT2,
          template <class> class t_TRAIT3,
          template <class> class t_TRAIT4,
          template <class> class t_TRAIT5,
          template <class> class t_TRAIT6,
          template <class> class t_TRAIT7,
          template <class> class t_TRAIT8,
          template <class> class t_TRAIT9>
struct SelectTrait_Imp {
    enum {
        ORDINAL = (t_TRAIT1<t_TYPE>::value   ? 1
                   : t_TRAIT2<t_TYPE>::value ? 2
                   : t_TRAIT3<t_TYPE>::value ? 3
                   : t_TRAIT4<t_TYPE>::value ? 4
                   : t_TRAIT5<t_TYPE>::value ? 5
                   : t_TRAIT6<t_TYPE>::value ? 6
                   : t_TRAIT7<t_TYPE>::value ? 7
                   : t_TRAIT8<t_TYPE>::value ? 8
                   : t_TRAIT9<t_TYPE>::value ? 9
                                             : 0)
    };

    typedef typename Switch<ORDINAL,
                            SelectTraitCase<>,
                            SelectTraitCase<t_TRAIT1>,
                            SelectTraitCase<t_TRAIT2>,
                            SelectTraitCase<t_TRAIT3>,
                            SelectTraitCase<t_TRAIT4>,
                            SelectTraitCase<t_TRAIT5>,
                            SelectTraitCase<t_TRAIT6>,
                            SelectTraitCase<t_TRAIT7>,
                            SelectTraitCase<t_TRAIT8>,
                            SelectTraitCase<t_TRAIT9> >::Type Type;
};

                             // ==================
                             // struct SelectTrait
                             // ==================

template <class t_TYPE,
          template <class> class t_TRAIT1,
          template <class> class t_TRAIT2 = SelectTrait_False,
          template <class> class t_TRAIT3 = SelectTrait_False,
          template <class> class t_TRAIT4 = SelectTrait_False,
          template <class> class t_TRAIT5 = SelectTrait_False,
          template <class> class t_TRAIT6 = SelectTrait_False,
          template <class> class t_TRAIT7 = SelectTrait_False,
          template <class> class t_TRAIT8 = SelectTrait_False,
          template <class> class t_TRAIT9 = SelectTrait_False>
struct SelectTrait : SelectTrait_Imp<t_TYPE,
                                     t_TRAIT1,
                                     t_TRAIT2,
                                     t_TRAIT3,
                                     t_TRAIT4,
                                     t_TRAIT5,
                                     t_TRAIT6,
                                     t_TRAIT7,
                                     t_TRAIT8,
                                     t_TRAIT9>::Type {
    // Instantiate each specified (template parameter) 't_TRAIT1' to 't_TRAIT9'
    // metafunction using the specified (template parameter) 't_TYPE'.  Inherit
    // from 'SelectTraitCase<TRAITx>', where *x* is '1' if
    // 't_TRAIT1<t_TYPE>::value' is true, '2' if 't_TRAIT2<t_TYPE>::value' is
    // true, etc..  If none of the traits evaluates to true, then inherit from
    // 'SelectTraitCase<>', which means that none of the traits match.

  public:
    enum {
        ORDINAL = SelectTrait_Imp<t_TYPE,
                                  t_TRAIT1,
                                  t_TRAIT2,
                                  t_TRAIT3,
                                  t_TRAIT4,
                                  t_TRAIT5,
                                  t_TRAIT6,
                                  t_TRAIT7,
                                  t_TRAIT8,
                                  t_TRAIT9>::ORDINAL
    };

    typedef bsl::integral_constant<int, ORDINAL> OrdinalType;
};

}  // close package namespace

}  // close enterprise namespace

#endif // ! defined(INCLUDED_BSLMF_SELECTTRAIT)

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