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

/// 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).
template <class t_TYPE, template <class t_T> class t_TRAIT, bool t_COND = true>
class NestedTraitDeclaration {

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