// bslmf_nestedtraitdeclaration.h                                     -*-C++-*-
#ifndef INCLUDED_BSLMF_NESTEDTRAITDECLARATION
#define INCLUDED_BSLMF_NESTEDTRAITDECLARATION

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

//@PURPOSE: Provide a nested declaration to associate a class with a trait.
//
//@MACROS:
//  BSLMF_NESTED_TRAIT_DECLARATION: macro that associates a trait with a class
//  BSLMF_NESTED_TRAIT_DECLARATION_IF: conditional macro to associates a trait
//
//@SEE_ALSO: bslmf_detectnestedtrait
//
//@DESCRIPTION: This component defines a pair of macros,
// 'BSLMF_NESTED_TRAIT_DECLARATION' and 'BSLMF_NESTED_TRAIT_DECLARATION_IF',
// that can be used in association with the facilities provided by
// 'bslmf_detectnestedtrait' to declare that a given class has a given trait.
//
// Traits provide a mechanism for convenient compile-time discovery of
// information about a class, which is useful in particular for providing
// efficient specializations of generalized containers and algorithms without
// having to rely on knowledge of specific target classes.
//
// The primary public interface of this component consists of two macros that
// provide a facility for declaring that a given class has a given trait.
// These macros embed the association between the type and the trait inside the
// class definition itself, hence the term "nested trait declaration".
//
// Note that the term "nested" is not meant to imply that this facility
// declares a nested type within the class namespace, only that the trait
// declaration appears as one of the public declarations that make up the class
// definition.  For example, we could declare that a class, 'xyza::Foo', has
// the trait 'abcd::BarTrait' 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
//..
// Two flavors of macro are provided: one for declaring unconditionally that a
// class has a trait, and another for declaring that the class has a trait if
// and only if a given compile-time expression evaluates to 'true'.
//
///Relationship with "C++11-style" Type Traits
///-------------------------------------------
// Traits declared using this component are not automatically compatible
// mechanisms designed to detect "C++11-style" traits.  For a full discussion
// of the relationship between nested traits and "C++11-style" traits, as well
// as best practices for defining, associating, and detecting traits, see the
// component documentation for 'bslmf_detectnestedtrait'.
//
///Usage
///-----
// This section illustrates the intended use of this component.
//
///Example 1: Testing a Type for 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, assume that a compatible trait, 'abcd::RequiresLockTrait', has been
// defined that indicates that a type's methods must not be called unless a
// known lock is first acquired:
//..
//  namespace abcd { template <class t_TYPE> struct RequiresLockTrait; }
//..
// The implementation of 'abcd::RequiresLockTrait' is not shown.
//
// Then, in package 'xyza', we declare a type, 'DoesNotRequireLockType', that
// can be used without acquiring the lock:
//..
//  namespace xyza {
//
//  class DoesNotRequireLockType {
//      // ...
//
//    public:
//      // CREATORS
//      DoesNotRequireLockType();
//          // ...
//  };
//..
// Next, we declare a type, 'RequiresLockType', that does require the lock.  We
// use the 'BSLMF_NESTED_TRAIT_DECLARATION' macro to associate the type with
// the 'abcd::RequiresLockTrait' trait:
//..
//  class RequiresLockType {
//      // ...
//
//    public:
//      // TRAITS
//      BSLMF_NESTED_TRAIT_DECLARATION(RequiresLockType,
//                                     abcd::RequiresLockTrait);
//
//      // CREATORS
//      RequiresLockType();
//          // ...
//
//  };
//..
// Notice that the macro declaration is performed within the scope of the class
// declaration, and must be done with public scope.
//
// Now, 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);
//
//      // ...
//  };
//
//  } // close package namespace
//..
// Finally, code interacting with 'xyza::DoesNotRequireLockType',
// 'xyza::RequiresLockType' or 'xyza::Container' objects will be able to choose
// the appropriate code path by checking for the 'abcd::RequiresLockTrait'
// trait.  See 'bslmf_detectnestedtrait' for an example of how generic code
// would use such a trait.

#include <bslscm_version.h>

namespace BloombergLP {

namespace bslmf {

                        // ============================
                        // class NestedTraitDeclaration
                        // ============================

template <class t_TYPE, template <class t_T> class t_TRAIT, bool t_COND = true>
class NestedTraitDeclaration {
    // Class 't_TYPE' will be convertible to
    // 'NestedTraitDeclaration<t_TYPE,t_TRAIT,true>' if 't_TRAIT' is associated
    // with 't_TYPE' using the 'BSLMF_NESTED_TRAIT_DECLARATION' macro.  Nested
    // trait detection depends on 't_COND' being true.  If 't_COND' is false,
    // the nested trait detection will not see the conversion it is looking for
    // and will not associate 't_TRAIT' with 't_TYPE'.  This feature is used by
    // 'BSLMF_NESTED_TRAIT_DECLARATION_IF' to turn a trait on or off depending
    // on a compile-time condition (usually another trait).

  public:
    // PUBLIC TYPES
    typedef NestedTraitDeclaration Type;

    // CREATORS
    //! NestedTraitDeclaration();
    //! NestedTraitDeclaration(const  NestedTraitDeclaration&);
    //! NestedTraitDeclaration& operator=(const  NestedTraitDeclaration&);
    //! ~NestedTraitDeclaration();
};

                    // ====================================
                    // macro BSLMF_NESTED_TRAIT_DECLARATION
                    // ====================================

#define BSLMF_NESTED_TRAIT_DECLARATION(t_TYPE, t_TRAIT)                       \
    operator BloombergLP::bslmf::NestedTraitDeclaration<t_TYPE, t_TRAIT>()    \
        const                                                                 \
    {                                                                         \
        return BloombergLP::bslmf::NestedTraitDeclaration<t_TYPE, t_TRAIT>(); \
    }

#ifdef __CDT_PARSER__
// Work around an Eclise CDT bug where it fails to parse the conditional trait
// declaration.  See internal DRQS 47839133.
#define BSLMF_NESTED_TRAIT_DECLARATION_IF(t_TYPE, t_TRAIT, t_COND)
#else
#define BSLMF_NESTED_TRAIT_DECLARATION_IF(t_TYPE, t_TRAIT, t_COND)            \
    operator BloombergLP::bslmf::                                             \
        NestedTraitDeclaration<t_TYPE, t_TRAIT, t_COND>() const               \
    {                                                                         \
        return BloombergLP::bslmf::                                           \
            NestedTraitDeclaration<t_TYPE, t_TRAIT, t_COND>();                \
    }
#endif

}  // close package namespace

}  // close enterprise namespace

#endif // ! defined(INCLUDED_BSLMF_NESTEDTRAITDECLARATION)

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