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

template <class t_COMPARATOR, class t_KEY, class = void>
struct IsTransparentPredicate : bsl::false_type {
    // 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>
struct IsTransparentPredicate<
    t_COMPARATOR,
    t_KEY,
    BSLMF_VOIDTYPE(typename t_COMPARATOR::is_transparent)> : bsl::true_type {
    // This specialization of 'IsTransparentPredicate', for when the (template
    // parameter) 't_COMPARATOR' is transparent, derives from '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 ----------------------------------