Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslmf_istransparentpredicate
[Package bslmf]

Support detection of whether a predicate functor is transparent. More...

Namespaces

namespace  bslmf

Detailed Description

Outline
Purpose:
Support detection of whether a predicate functor is transparent.
Classes:
bslmf::IsTransparentPredicate Detects is_transparent
See also:
Component bslstl_map, Component 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());