// bslmf_detectnestedtrait.h                                          -*-C++-*-
#ifndef INCLUDED_BSLMF_DETECTNESTEDTRAIT
#define INCLUDED_BSLMF_DETECTNESTEDTRAIT

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

//@PURPOSE: Provide a facility for defining traits and detecting legacy traits.
//
//@CLASSES:
//   bslmf::DetectNestedTrait: meta-function to test a nested trait
//
//@SEE_ALSO: bslmf_nestedtraitdeclaration
//
//@DESCRIPTION: This component defines a meta-function,
// 'bslmf::DetectNestedTrait', that facilitates the creation of traits that can
// be associated with a type using the nested trait mechanism in
// 'bslmf_declarenestedtrait'.  Such traits are referred to as "nested traits"
// because their association with a type is embedded within the type's
// definition.  'bslmf::DetectNestedTrait' and can also be used to detect
// traits created using older legacy traits definitions mechanisms used at
// Bloomberg.
//
// Please note:
//: 1. The use of 'bslmf::DetectNestedTrait' to detect traits is *deprecated*.
//:    Clients should detect traits using the C++11 idiom (see
//:    {Nested Trait Idiom vs. C++11 Trait Idiom} below)
//:
//: 2. Clients are encouraged to use a C++11 idiom for defining traits.
//:    However, authors of traits who want trait users to be able to take
//:    advantage of 'bslmf_nestedtraitdeclaration' must define traits that
//:    inherit from 'bslmf::DetectNestedTrait' (see
//:    {Writing a User-Defined Trait} below).
//
///Nested Trait Idiom vs. C++11 Trait Idiom
///----------------------------------------
// BDE supports two idioms for defining traits and associating them with types.
// The older idiom uses 'bslmf::DetectNestedTrait' to define traits, and the
// 'BSLMF_NESTED_TRAIT_DECLARATION*' macros to associate traits with types.
// This idiom is called the "nested trait" idiom.
//
// The newer idiom is familiar to users of C++11 traits, and is referred to
// here as the "C++11 trait" idiom.  In the C++11 trait idiom, a trait is a
// template that derives its truth value from 'bsl::true_type' or
// 'bsl::false_type', and is associated with a type by providing a
// specialization of the trait for the associated type.
//
// For example, a minimal C++11 trait, 'abcd::C11Trait', could be defined as:
//..
//  namespace abcd {
//
//  template <class t_TYPE>
//  struct C11Trait : bsl::false_type {
//  };
//
//  }  // close namespace abcd
//..
// 'abcd::C11Trait' would then be associated with a class, 'xyza::SomeClass',
// by specializing the trait for that class:
//..
//  namespace xyza {
//
//  class SomeClass {
//      // The definition of 'SomeClass' does not affect the trait mechanism.
//
//      // ...
//  };
//
//  }  // close namespace xyza
//
//  namespace abcd {
//
//  template <>
//  struct C11Trait<xyza::SomeClass> : bsl::true_type {
//  };
//
//  }  // close namespace abcd
//..
// Note that the specialization is defined in the same namespace as the
// original trait.
//
// Both idioms detect the association of a trait with a class in the same way:
// by inspecting the trait's 'value' member.
//..
//  assert(true  == abcd::C11Trait<xyza::SomeClass>::value);
//  assert(false == abcd::C11Trait<xyza::Foo>::value);
//  assert(true  == abcd::BarTrait<xyza::Foo>::value);
//  assert(false == abcd::BarTrait<xyza::SomeClass>::value);
//..
// The C++11 trait idiom is the standard idiom for new code in BDE.
//
///Writing a User-Defined Trait
///----------------------------
// On systems that do not require compatibility with the nested trait idiom,
// new traits should be written according to the C++11 trait idiom.
//
// On systems that support the nested trait idiom, any new user-defined trait
// should derive its truth value from 'bslmf::DetectNestedTrait' following the
// Curiously Recurring Template Pattern.  This will allow the trait to be
// detected by directly inspecting the trait's 'value' member, regardless of
// whether the trait is associated with a type through the nested trait idiom
// or through the C++11 trait idiom.
//
// Therefore, the simplest maximally-compatible trait would look like this:
//..
//  template <class t_TYPE>
//  struct MyTrait : bslmf::DetectNestedTrait<t_TYPE, MyTrait>::type {};
//..
// A trait having more complex default logic could derive from
// 'bsl::integral_constant' using the 'value' member of
// 'bslmf::DetectNestedTrait', such as:
//..
//  template <class t_TYPE>
//  struct ComplexTrait : bsl::integral_constant<bool,
//                       bslmf::DetectNestedTrait<t_TYPE, ComplexTrait>::value
//                       || SomeOtherTrait<t_TYPE>::value> {
//  };
//..
// These are the only recommended uses of
// 'bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::type' and
// 'bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::value'.
//
///Detecting Legacy Traits
///-----------------------
// If a trait, 't_TRAIT', has been associated with a type, 't_TYPE', using one
// of the 'BSLMF_NESTED_TRAIT_DECLARATION*' macros then
// 'bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>' derives from 'bsl::true_type'.
// Otherwise, 'bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>' derives from
// 'bsl::false_type'.
//
// Therefore, if a trait 'abcd::BarTrait' has been associated with a class
// 'xyza::Foo' in the following way:
//..
//  namespace xyza {
//
//  class Foo {
//      // ... various implementation details ...
//
//    public:
//      // TRAITS
//      BSLMF_NESTED_TRAIT_DECLARATION(Foo, abcd::BarTrait);
//
//      // ... the rest of the public interface ...
//  };
//
//  }  // close namespace xyza
//..
// then 'bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::value' will evaluate to
// 'true' and 'bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::type' will be
// 'bsl::true_type'.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Defining a Custom Trait
/// - - - - - - - - - - - - - - - - -
// When writing generic infrastructure code, we often need to choose among
// multiple code paths based on the capabilities of the types on which we are
// operating.  If those capabilities are reflected in a type's public
// interface, we may be able to use techniques such as SFINAE to choose the
// appropriate code path.  However, SFINAE cannot detect all of a type's
// capabilities.  In particular, SFINAE cannot detect constructors, memory
// allocation, thread-safety characteristics, and so on.  Functions that depend
// on these capabilities must use another technique to determine the correct
// code path to use for a given type.  We can solve this sort of problem by
// associating types with custom traits that indicate what capabilities are
// provided by a given type.
//
// First, in package 'abcd', define a trait, 'RequiresLockTrait', that
// indicates that a type's methods must not be called unless a known lock it
// first acquired:
//..
//  namespace abcd {
//
//  template <class t_TYPE>
//  struct RequiresLockTrait :
//                  bslmf::DetectNestedTrait<t_TYPE, RequiresLockTrait>::type {
//  };
//
//  } // close package namespace
//..
// Notice that 'RequiresLockTrait' derives from
// 'bslmf::DetectNestedTrait<t_TYPE, RequiresLockTrait>::type' using the
// curiously recurring template pattern.
//
// Then, in package 'xyza', we declare a type, 'DoesNotRequireALockType', that
// can be used without acquiring the lock:
//..
//  namespace xyza {
//
//  class DoesNotRequireLockType {
//      // ...
//
//    public:
//      // CREATORS
//      DoesNotRequireLockType();
//          // ...
//  };
//..
// Next, we declare a type, 'RequiresLockTypeA', that does require the lock.
// We use the 'BSLMF_NESTED_TRAIT_DECLARATION' macro to associate the type with
// the 'abcd::RequiresLockTrait' trait:
//..
//  class RequiresLockTypeA {
//      // ...
//
//    public:
//      // TRAITS
//      BSLMF_NESTED_TRAIT_DECLARATION(RequiresLockTypeA,
//                                     abcd::RequiresLockTrait);
//
//      // CREATORS
//      RequiresLockTypeA();
//          // ...
//
//  };
//..
// Notice that the macro declaration is performed within the scope of the class
// declaration, and must be done with public scope.
//
// Then, we declare a templatized container type, 'Container', that is
// parameterized on some 'ELEMENT' type.  If 'ELEMENT' requires a lock, then a
// 'Container' of 'ELEMENT's will require a lock as well.  This can be
// expressed using the 'BSLMF_NESTED_TRAIT_DECLARATION_IF' macro, by providing
// 'abcd::RequiresLockTrait<ELEMENT>::value' as the condition for associating
// the trait with 'Container'.
//..
//  template <class ELEMENT>
//  struct Container {
//      // ...
//
//    public:
//      // TRAITS
//      BSLMF_NESTED_TRAIT_DECLARATION_IF(Container, abcd::RequiresLockTrait,
//                                    abcd::RequiresLockTrait<ELEMENT>::value);
//
//      // ...
//  };
//..
// Next, we show that traits based on 'bslmf::DetectNestedTrait' can be
// associated with a type using "C++11-style" trait association.  To do this,
// we declare a type, 'RequiresLockTypeB', that also requires the lock, but
// does not used the 'BSLMF_NESTED_TRAIT_DECLARATION' macro:
//..
//  class RequiresLockTypeB {
//      // ...
//
//    public:
//      // CREATORS
//      RequiresLockTypeB();
//          // ...
//
//  };
//
//  } // close package namespace
//..
// Then, we associate 'RequiresLockTypeB' with 'abcd::RequiresLockTrait' by
// directly specializing 'abcd::RequiresLockTrait<xyza::RequiresLockTypeB>'.
// This is the standard way of associating a type with a trait since C++11:
//..
//  namespace abcd {
//
//  template <>
//  struct RequiresLockTrait<xyza::RequiresLockTypeB> : bsl::true_type {
//  };
//
//  } // close namespace abcd
//..
// Now, we can write a function that inspects
// 'abcd::RequiresLockTrait<t_TYPE>::value' to test whether or not various
// types are associated with 'abcd::RequiresLockTrait':
//..
//  void example1()
//  {
//      assert(false ==
//             abcd::RequiresLockTrait<xyza::DoesNotRequireLockType>::value);
//
//      assert(true  ==
//             abcd::RequiresLockTrait<xyza::RequiresLockTypeA>::value);
//
//      assert(true  ==
//             abcd::RequiresLockTrait<xyza::RequiresLockTypeB>::value);
//
//      assert(false ==
//             abcd::RequiresLockTrait<
//                     xyza::Container<xyza::DoesNotRequireLockType> >::value);
//
//      assert(true  ==
//             abcd::RequiresLockTrait<
//                          xyza::Container<xyza::RequiresLockTypeA> >::value);
//
//      assert(true  ==
//             abcd::RequiresLockTrait<
//                          xyza::Container<xyza::RequiresLockTypeB> >::value);
//
//      // ...
//  }
//..
// Finally, we demonstrate that the trait can be tested at compilation time, by
// writing a function that tests the trait within the context of a compile-time
// 'BSLMF_ASSERT':
//..
//  void example2()
//  {
//      BSLMF_ASSERT(false ==
//             abcd::RequiresLockTrait<xyza::DoesNotRequireLockType>::value);
//
//      BSLMF_ASSERT(true  ==
//             abcd::RequiresLockTrait<xyza::RequiresLockTypeA>::value);
//
//      BSLMF_ASSERT(true  ==
//             abcd::RequiresLockTrait<xyza::RequiresLockTypeB>::value);
//
//      BSLMF_ASSERT(false ==
//             abcd::RequiresLockTrait<
//                     xyza::Container<xyza::DoesNotRequireLockType> >::value);
//
//      BSLMF_ASSERT(true  ==
//             abcd::RequiresLockTrait<
//                          xyza::Container<xyza::RequiresLockTypeA> >::value);
//
//      BSLMF_ASSERT(true  ==
//             abcd::RequiresLockTrait<
//                          xyza::Container<xyza::RequiresLockTypeB> >::value);
//
//  }
//..

#include <bslscm_version.h>

#include <bslmf_integralconstant.h>
#include <bslmf_isconvertibletoany.h>
#include <bslmf_matchanytype.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_voidtype.h>

namespace BloombergLP {
namespace bslmf {

                        // ===========================
                        // class DetectNestedTrait_Imp
                        // ===========================

template <class t_TYPE, template <class> class t_TRAIT, class = void>
struct DetectNestedTrait_Imp {
    // Metafunction to detect whether the specified 't_TRAIT' parameter is
    // associated with the specified 't_TYPE' parameter using the nested type
    // trait mechanism.  The nested 'type' is an alias for 'bsl::false_type'
    // unless 't_TYPE' is a class type that the user has associated with the
    // specified trait using the 'BSLMF_NESTED_TRAIT_DECLARATION' macro.  Class
    // types detect such association in the specialization below.

    // PUBLIC TYPES
    typedef bsl::false_type type;
};

template <class t_TYPE, template <class> class t_TRAIT>
struct DetectNestedTrait_Imp<t_TYPE, t_TRAIT, BSLMF_VOIDTYPE(int t_TYPE::*)> {
    // This template specialization detects whether the specified 't_TRAIT'
    // parameter is associated with the specified 't_TYPE' parameter, which is
    // constrained to be a class type, using the nested type trait mechanism.
    // The nested 'type' is an alias for 'bsl::true_type' if and only if
    // 't_TYPE' is associated with 't_TRAIT' using the
    // 'BSLMF_NESTED_TRAIT_DECLARATION' macro, and for 'bsl::false_type'
    // otherwise.  Note that nested trait associations are not inherited by
    // derived classes, which must be associated in their own right.

  private:
    // PRIVATE CLASS METHODS
    static char check(NestedTraitDeclaration<t_TYPE, t_TRAIT>, int);
        // Declared but not defined.  This overload is selected if called with
        // a type convertible to 'NestedTraitDeclaration<t_TYPE, t_TRAIT>'

    static int check(MatchAnyType, ...);
        // Declared but not defined.  This overload is selected if called with
        // a type not convertible to 'NestedTraitDeclaration<t_TYPE, t_TRAIT>'

  private:
    // PRIVATE TYPES
    enum { k_ENSURE_TYPE_IS_COMPLETE = sizeof(t_TYPE) };

    enum {
        k_CONVERTIBLE_TO_NESTED_TRAIT = sizeof(check(TypeRep<t_TYPE>::rep(),
                                                     0)) == sizeof(char),
        k_CONVERTIBLE_TO_ANY_TYPE     = IsConvertibleToAny<t_TYPE>::value
    };

    enum {
        k_VALUE = k_CONVERTIBLE_TO_NESTED_TRAIT && !k_CONVERTIBLE_TO_ANY_TYPE
        // Non-zero if 't_TRAIT' is associated with 't_TYPE' using the nested
        // type trait mechanism; otherwise zero.
    };

  public:
    // PUBLIC TYPES
    typedef bsl::integral_constant<bool, k_VALUE> type;
        // Type representing the result of this metafunction.  An alias for
        // 'bsl::true_type' if 't_TRAIT' is associated with 't_TYPE' using the
        // nested type trait mechanism; otherwise an alias for
        // 'bsl::false_type'.
};

                        // =======================
                        // class DetectNestedTrait
                        // =======================

template <class t_TYPE, template <class> class t_TRAIT>
struct DetectNestedTrait : DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
    // This 'struct' template metafunction detects whether the specified
    // 't_TRAIT' parameter is associated with the specified 't_TYPE' parameter
    // using the nested type trait mechanism.  This trait derives from
    // 'bsl::true_type' if and only if 't_TYPE' is a class type that associated
    // with the specified trait using the 'BSLMF_NESTED_TRAIT_DECLARATION'
    // macro, and from 'bsl::false_type' otherwise.  Users should not
    // specialize this trait directly for their types, but should always use
    // the macro to make a nested trait association.
};

template <class t_TYPE, template <class> class t_TRAIT>
struct DetectNestedTrait<const t_TYPE, t_TRAIT>
: DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
};
template <class t_TYPE, template <class> class t_TRAIT>
struct DetectNestedTrait<volatile t_TYPE, t_TRAIT>
: DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
};
template <class t_TYPE, template <class> class t_TRAIT>
struct DetectNestedTrait<const volatile t_TYPE, t_TRAIT>
: DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
};
    // Partial specializations to ensure that cv-qualified types have the same
    // association with a nested trait as their unqualified counterpart.  Note
    // that we delegate directly to the '_Imp' class template, rather than back
    // to the primary template, to avoid an infinite recursion bug on platforms
    // that do not correctly implement "abominable" function types with
    // cv-qualifiers.

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

#endif

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