// bslmf_istransparentpredicate.h                                     -*-C++-*-
#ifndef INCLUDED_BSLMF_ISTRANSPARENTPREDICATE
#define INCLUDED_BSLMF_ISTRANSPARENTPREDICATE

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

//@PURPOSE: Support detection of whether a predicate functor is transparent.
//
//@CLASSES:
//  bslmf::IsTransparentPredicate: Detects `is_transparent`
//
//@SEE_ALSO: bslstl_map, bslstl_set
//
//@DESCRIPTION: This component provides a metafunction,
// `bslmf::IsTransparentPredicate`, that can be used to detect whether a
// comparator is transparent (supports heterogeneous comparisons).  If the
// comparator has a nested type named `is_transparent`, the template inherits
// from `true_type`, otherwise it inherits from `false_type`.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Specifying Behavior Of Comparator
/// - - - - - - - - - - - - - - - - - - - - - -
// In this example, we demonstrate the use of `IsTransparentPredicate` to
// determine different comparator's behavior.  Our goal is to create an
// overload of an associative container's method that participates in overload
// resolution only if the comparator is transparent, otherwise we fall back on
// a default behavior.
//
// First, we define simple container, `Vector`, that is used as a foundation
// for our associative container:
// ```
// template <class TYPE>
// class Vector {
//     // DATA
//     const TYPE * d_begin_p;   // pointer to the beginning
//     const TYPE * d_end_p;     // pointer to the end
//
//   public:
//     // CREATORS
//     Vector(const TYPE *first, const TYPE *last)
//         // Construct an object that references the specified 'first' and
//         // 'last' items in the sequence.
//     : d_begin_p(first)
//     , d_end_p(last)
//     {
//     }
//
//     // ACCESSORS
//     const TYPE *begin() const
//         // Return a reference providing non-modifiable access to the first
//         // object in the underlying sequence.
//     {
//         return d_begin_p;
//     }
//
//     const TYPE *end() const
//         // Return a reference providing non-modifiable access to the last
//         // object in the underlying sequence.
//     {
//         return d_end_p;
//     }
// };
// ```
// Then we define simple type, `String`, that is used as a key.  Note that we
// have comparison operators that allow to compare both two `String` objects
// and `String` object with character sequence.
// ```
// class String {
//     // CLASS DATA
//     static int  s_numObjectsCreated;  // total number of created instances
//
//     // DATA
//     const char *d_data_p;             // reference to a string
//
//   public:
//     // CLASS METHODS
//     static int numObjectsCreated()
//         // Return the total number of created instances of class 'String'.
//     {
//         return s_numObjectsCreated;
//     }
//
//     // CREATORS
//     String()
//         // Construct an empty string
//     : d_data_p("")
//     {
//         ++s_numObjectsCreated;
//     }
//
//     String(const char *data)  // IMPLICIT
//         // Construct a string that references the specified 'data'.  The
//         // behavior is undefined unless 'data' points to a null-terminated
//         // string.
//     : d_data_p(data)
//     {
//         ++s_numObjectsCreated;
//     }
//
//     // FREE FUNCTIONS
//     friend bool operator<(const String& lhs, const String& rhs)
//         // Return 'true' if the value of the specified 'lhs' string is
//         // lexicographically less than that of the specified 'rhs' string,
//         // and 'false' otherwise.
//     {
//         const char *lhsData = lhs.d_data_p;
//         const char *rhsData = rhs.d_data_p;
//         while (true) {
//             if (*lhsData < *rhsData) {
//                 return true;                                      // RETURN
//             }
//             else if (*lhsData > *rhsData || *lhsData == '\0') {
//                 return false;                                     // RETURN
//             }
//             ++lhsData;
//             ++rhsData;
//         }
//     }
//
//     friend bool operator<(const String& lhs, const char *rhs)
//         // Return 'true' if the value of the specified 'lhs' string is
//         // lexicographically less than the specified 'rhs' character
//         // sequence, and 'false' otherwise.
//     {
//         const char *lhsData = lhs.d_data_p;
//         while (true) {
//             if (*lhsData < *rhs) {
//                 return true;                                      // RETURN
//             }
//             else if (*lhsData > *rhs || *lhsData == '\0') {
//                 return false;                                     // RETURN
//             }
//             ++lhsData;
//             ++rhs;
//         }
//     }
//
//     friend bool operator<(const char *lhs, const String& rhs)
//         // Return 'true' if the specified 'lhs' character sequence is
//         // lexicographically less than the value of the specified 'rhs'
//         // string, and 'false' otherwise.
//     {
//         const char *rhsData = rhs.d_data_p;
//         while (true) {
//             if (*lhs < *rhsData) {
//                 return true;                                      // RETURN
//             }
//             else if (*lhs > *rhsData || *lhs == '\0') {
//                 return false;                                     // RETURN
//             }
//             ++lhs;
//             ++rhsData;
//         }
//     }
// };
//
// int String::s_numObjectsCreated = 0;
// ```
// Next we define our associative container.  Note that we use
// `IsTransparentPredicate` for a method with `enable_if` in the return type.
// This adds our function template into the function overload resolution set if
// and only if the comparison function object is considered transparent, using
// a technique known as "SFINAE".
// ```
// template <class t_COMPARATOR>
// class FlatStringSet {
//
//     // DATA
//     t_COMPARATOR   d_comparator;  // comparison object
//     Vector<String> d_data;        // stores the data
//
//   public:
//     // TYPES
//     typedef const String * const_iterator;
//
//     // CREATORS
//     template <class INPUT_ITERATOR>
//     FlatStringSet(INPUT_ITERATOR    first,
//                   INPUT_ITERATOR    last,
//                   const t_COMPARATOR& comparator = t_COMPARATOR());
//         // Create a set, and insert each 'String' object in the sequence
//         // starting at the specified 'first' element, and ending
//         // immediately before the specified 'last' element, ignoring those
//         // keys having a value equivalent to that which appears earlier in
//         // the sequence.  Optionally specify a 'comparator' used to order
//         // keys contained in this object.  If 'comparator' is not supplied,
//         // a default-constructed object of the (template parameter) type
//         // 't_COMPARATOR' is used.  This operation has 'O[N]' complexity,
//         // where 'N' is the number of elements between 'first' and 'last'.
//         // The (template parameter) type 'INPUT_ITERATOR' shall meet the
//         // requirements of an input iterator defined in the C++11 standard
//         // [24.2.3] providing access to values of a type convertible to
//         // 'String', and 'String' must be 'emplace-constructible' from '*i'
//         // into this set, where 'i' is a dereferenceable iterator in the
//         // range '[first .. last)'.  The behavior is undefined unless
//         // 'first' and 'last' refer to a sequence of valid values where
//         // 'first' is at a position at or before 'last', the range is
//         // ordered according to 't_COMPARATOR', and there are no duplicates
//         // in the range.
//
//     // ACCESSORS
//     const_iterator begin() const;
//         // Return an iterator providing non-modifiable access to the first
//         // 'String' object in the ordered sequence of 'String' objects
//         // maintained by this set, or the 'end' iterator if this set is
//         // empty.
//
//     const_iterator end() const;
//         // Return an iterator providing non-modifiable access to the
//         // past-the-end element in the ordered sequence of 'String' objects
//         // maintained by this set.
//
//     const_iterator find(const String& key) const
//         // Return an iterator to the element that compares equal to the
//         // specified 'key' if such an element exists, and an end iterator
//         // otherwise.
//         //
//         // Note: implemented inline due to Sun CC compilation error.
//     {
//         for (const_iterator first = begin(); first != end(); ++first) {
//             if (d_comparator(key, *first)) {
//                 return end();                                     // RETURN
//             }
//             if (!d_comparator(*first, key)) {
//                 return first;                                     // RETURN
//             }
//         }
//         return end();
//     }
//
//     template <class t_KEY>
//     typename bsl::enable_if<IsTransparentPredicate<t_COMPARATOR,
//                                                    t_KEY>::value,
//                             const_iterator>::type
//     find(const t_KEY& key) const
//         // Return an iterator to the element that compares equal to the
//         // specified 'key' if such an element exists, and an end iterator
//         // otherwise.
//         //
//         // Note: implemented inline due to Sun CC compilation error.
//     {
//         for (const_iterator first = begin(); first != end(); ++first) {
//             if (d_comparator(key, *first)) {
//                 return end();                                     // RETURN
//             }
//             if (!d_comparator(*first, key)) {
//                 return first;                                     // RETURN
//             }
//         }
//         return end();
//     }
//
//     int size() const;
//         // Return the number of elements in this container.
// };
//
// // CREATORS
// template <class t_COMPARATOR>
// template <class INPUT_ITERATOR>
// FlatStringSet<t_COMPARATOR>::FlatStringSet(INPUT_ITERATOR    first,
//                                          INPUT_ITERATOR    last,
//                                          const t_COMPARATOR& comparator)
// : d_comparator(comparator),
//   d_data(first, last)
// {
// }
//
// // ACCESSORS
// template <class t_COMPARATOR>
// typename FlatStringSet<t_COMPARATOR>::const_iterator
// FlatStringSet<t_COMPARATOR>::begin() const
// {
//     return d_data.begin();
// }
//
// template <class t_COMPARATOR>
// typename FlatStringSet<t_COMPARATOR>::const_iterator
// FlatStringSet<t_COMPARATOR>::end() const
// {
//     return d_data.end();
// }
//
// template <class t_COMPARATOR>
// int FlatStringSet<t_COMPARATOR>::size() const
// {
//     return static_cast<int>(end() - begin());
// }
// ```
// Then we define two comparators.  These classes are completely identical
// except that one defines an `is_transparent` type, and the other does not.
// ```
// struct TransparentComp
//     // This class can be used as a comparator for containers.  It has a
//     // nested type 'is_transparent', so it is classified as transparent by
//     // the 'bslmf::IsTransparentPredicate' metafunction and can be used for
//     // heterogeneous comparison.
// {
//     // TYPES
//     typedef void is_transparent;
//
//     // ACCESSORS
//     template <class LHS, class RHS>
//     bool operator()(const LHS& lhs, const RHS& rhs) const
//         // Return 'true' if the specified 'lhs' is less than the specified
//         // 'rhs' and 'false' otherwise.
//     {
//         return lhs < rhs;
//     }
// };
//
// struct NonTransparentComp
//     // This class can be used as a comparator for containers.  It has no
//     // nested type 'is_transparent', so it is classified as
//     // non-transparent by the 'bslmf::IsTransparentPredicate' metafunction
//     // and can not be used for heterogeneous comparison.
// {
//     template <class LHS, class RHS>
//     bool operator()(const LHS& lhs, const RHS& rhs) const
//         // Return 'true' if the specified 'lhs' is less than the specified
//         // 'rhs' and 'false' otherwise.
//     {
//         return lhs < rhs;
//     }
// };
// ```
// Next we create an array of `String` objects and two `FlatStringSet` objects,
// basing on this array and having different comparators, transparent and
// non-transparent.  Note that creation of `FlatStringSet` object does not
// increase number of created `String` objects, because it just holds pointers
// to these objects, but does not copy them.
// ```
// int OBJECTS_NUMBER = String::numObjectsCreated();
// assert(0 == OBJECTS_NUMBER);
//
// String data[] = { "1", "2", "3", "5" };
// enum { dataSize = sizeof data / sizeof *data };
//
// assert(OBJECTS_NUMBER + 4 == String::numObjectsCreated());
// OBJECTS_NUMBER = String::numObjectsCreated();
//
// FlatStringSet<NonTransparentComp> nts(data, data + dataSize);
// FlatStringSet<   TransparentComp>  ts(data, data + dataSize);
//
// assert(4                  == nts.size()                 );
// assert(4                  ==  ts.size()                 );
// assert(OBJECTS_NUMBER     == String::numObjectsCreated());
// ```
// Then we call `find` method of set, having non-transparent comparator.
// Explicit creation of temporary `String` object predictably increases the
// number of created `String` objects.  But using character sequence as a
// parameter for `find` method increases it too.  Because comparison operator
// that accepts two `String` objects is used instead of operator that accepts
// `String` and char sequence, so `String` constructor is called implicitly.
// ```
// assert(nts.begin() + 0    == nts.find(String("1"))      );
// assert(nts.begin() + 1    == nts.find(String("2"))      );
// assert(nts.begin() + 2    == nts.find(String("3"))      );
// assert(nts.begin() + 3    == nts.find(String("5"))      );
// assert(nts.end()          == nts.find(String("6"))      );
//
// assert(OBJECTS_NUMBER + 5 == String::numObjectsCreated());
// OBJECTS_NUMBER = String::numObjectsCreated();
//
// assert(nts.begin() + 0    == nts.find(       "1" )      );
// assert(nts.begin() + 1    == nts.find(       "2" )      );
// assert(nts.begin() + 2    == nts.find(       "3" )      );
// assert(nts.begin() + 3    == nts.find(       "5" )      );
// assert(nts.end()          == nts.find(       "6" )      );
//
// assert(OBJECTS_NUMBER + 5 == String::numObjectsCreated());
// OBJECTS_NUMBER = String::numObjectsCreated();
// ```
// Finally we call `find` method of set, having transparent comparator.
// Explicit creation of temporary `String` object still increases the number of
// created `String` objects.  But using character sequence as a parameter for
// `find` method does not.  Because comparison operator that accepts `String`
// and char sequence is available and more appropriate then operator accepting
// two `String` objects.  So there is no need for implicit constructor
// invocation.
// ```
// assert( ts.begin() + 0    ==  ts.find(String("1"))      );
// assert( ts.begin() + 1    ==  ts.find(String("2"))      );
// assert( ts.begin() + 2    ==  ts.find(String("3"))      );
// assert( ts.begin() + 3    ==  ts.find(String("5"))      );
// assert( ts.end()          ==  ts.find(String("6"))      );
//
// assert(OBJECTS_NUMBER + 5 == String::numObjectsCreated());
// OBJECTS_NUMBER = String::numObjectsCreated();
//
// assert( ts.begin() + 0    ==  ts.find(        "1" )     );
// assert( ts.begin() + 1    ==  ts.find(        "2" )     );
// assert( ts.begin() + 2    ==  ts.find(        "3" )     );
// assert( ts.begin() + 3    ==  ts.find(        "5" )     );
// assert( ts.end()          ==  ts.find(        "6" )     );
//
// assert(OBJECTS_NUMBER     == String::numObjectsCreated());
// ```

#include <bslmf_integralconstant.h>
#include <bslmf_voidtype.h>

namespace BloombergLP {
namespace bslmf {

/// This `struct` template implements a meta-function to determine whether
/// the (template parameter) `t_COMPARATOR` is transparent (has a publicly
/// accessible member that is a type named `is_transparent`).  This generic
/// default template derives from `bsl::false_type`.  Template
/// specializations are provided (below) that derive from `bsl::true_type`.
template <class t_COMPARATOR, class t_KEY, class = void>
struct IsTransparentPredicate : bsl::false_type {
};

/// This specialization of `IsTransparentPredicate`, for when the (template
/// parameter) `t_COMPARATOR` is transparent, derives from `bsl::true_type`.
template <class t_COMPARATOR, class t_KEY>
struct IsTransparentPredicate<
    t_COMPARATOR,
    t_KEY,
    BSLMF_VOIDTYPE(typename t_COMPARATOR::is_transparent)> : bsl::true_type {
};

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