// bdlb_transformiterator.h                                           -*-C++-*-
#ifndef INCLUDED_BDLB_TRANSFORMITERATOR
#define INCLUDED_BDLB_TRANSFORMITERATOR

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

//@PURPOSE: Provide a wrapping iterator that invokes a functor on dereference.
//
//@CLASSES:
//  bdlb::TransformIterator: functor-invoking iterator wrapper
//  bdlb::TransformIteratorUtil: utility for creating transform iterators
//
//@DESCRIPTION: This component implements a class template,
// 'bdlb::TransformIterator', that stores an underlying iterator and a
// one-argument functor.  Iterator operations are passed through to the
// underlying iterator, with the exception of dereference.  For dereference,
// the functor is invoked on the result of dereferencing the underlying
// iterator, and the result of the functor invocation is returned.  This
// component also implements a utility class, 'bdlb::TransformIteratorUtil',
// that provides a function template for creating 'TransformIterator' objects.
//
// The templates expect two parameters.  The first parameter, designated
// 'FUNCTOR', is the type of a callable object that can be invoked with a
// single argument.  When compiling with C++03, this type must be either a
// function pointer or otherwise have a type from which 'bslmf::ResultType' can
// determine the result type of invoking the functor (see
// {'bslmf_resulttype'}).  The second parameter, designated 'ITERATOR', is the
// type of an object that models an iterator from which values may be obtained,
// i.e., a type such that 'bsl::iterator_traits<ITERATOR>' exists and for which
// 'typename bsl::iterator_traits<ITERATOR>::iterator_category' derives from
// 'bsl::input_iterator_tag' (see {'bslstl_iterator'}).  Note that object
// pointer types qualify.
//
// Note that 'bdlb::TransformIterator' is more useful in C++11 or later than in
// C++03, because lambdas can be used as function objects to match a 'FUNCTOR'
// of type 'bsl::function<RETURN_TYPE(INPUT_TYPE)>'.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Totaling a Grocery List
/// - - - - - - - - - - - - - - - - -
// Suppose we have a shopping list of products and we want to compute how much
// it will cost to buy selected items.  We can use 'bdlb::TransformIterator' to
// do the computation, looking up the price of each item.
//
// First, we set up the price list:
//..
//  bsl::map<bsl::string, double> prices;
//  prices["pudding"] = 1.25;
//  prices["apple"] = 0.33;
//  prices["milk"] = 2.50;
//..
// Then, we set up our shopping list:
//..
//  bsl::list<bsl::string> list;
//  list.push_back("milk");
//  list.push_back("milk");
//  list.push_back("pudding");
//..
// Next, we create a functor that will return a price given a product.  The
// following rather prolix functor at namespace scope is necessary for C++03:
//..
//  #ifndef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE
//  class Pricer {
//    private:
//      // DATA
//      const bsl::map<bsl::string, double> *d_prices_p;  // price list
//
//    public:
//      // PUBLIC TYPES
//      typedef double result_type;
//
//      // CREATORS
//      explicit Pricer(const bsl::map<bsl::string, double> *prices);
//          // Create a 'Pricer' object using the specified 'prices'.  The
//          // lifetime of 'prices' must be at least as long as this object.
//
//      // ACCESSORS
//      double operator()(const bsl::string& product) const;
//          // Return the price of the specified 'product'.
//  };
//
//  // CREATORS
//  Pricer::Pricer(const bsl::map<bsl::string, double> *prices)
//  : d_prices_p(prices)
//  {
//  }
//
//  double Pricer::operator()(const bsl::string& product) const
//  {
//      bsl::map<bsl::string, double>::const_iterator i =
//                                                   d_prices_p->find(product);
//      return i == d_prices_p->end() ? 0.0 : i->second;
//  }
//  #endif
//..
// Then, we create the functor object.  In C++11 or later, the explicit functor
// class above is unnecessary since we can use a lambda:
//..
//  #ifndef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE
//  Pricer pricer(&prices);
//  #else
//  auto pricer = [&](const bsl::string &product) { return prices[product]; };
//  #endif
//..
// Now, we need a pair of transform iterators to process our grocery list.  We
// can use 'TransformIteratorUtil::make' to create those iterators, avoiding
// the need to explicitly name types.  We create the iterators and process the
// list in one step, as follows:
//..
//  double total = bsl::accumulate(
//      bdlb::TransformIteratorUtil::make(list.begin(), pricer),
//      bdlb::TransformIteratorUtil::make(list.end(), pricer),
//      0.0);
//..
// Finally, we verify that we have the correct total:
//..
//  assert(6.25 == total);
//..
//
///Example 2: Totaling the Grocery List Again
/// - - - - - - - - - - - - - - - - - - - - -
// In the previous example, we did not explicitly name our iterator type.  We
// may want to do so, however, if we intend to reuse iterators, or store them
// in data structures.  We will rework the previous example using explicitly
// typed iterators.  We also demonstrate how a single iterator type can deal
// with multiple functors.
//
// First, we notice that we have two different functor types depending on
// whether we compile as C++03 or C++11.  To abstract away the difference, we
// will use a 'bsl::function' functor type that is conformable to both:
//..
//  typedef bdlb::TransformIterator<bsl::function<double(const bsl::string&)>,
//                                  bsl::list<bsl::string>::iterator> Iterator;
//..
// Then, we create a pair of these iterators to traverse our list:
//..
//  Iterator groceryBegin(list.begin(), pricer);
//  Iterator groceryEnd(list.end(), pricer);
//..
// Now, we add up the prices of our groceries:
//..
//  double retotal = bsl::accumulate(groceryBegin, groceryEnd, 0.0);
//..
// Finally, we verify that we have the correct total:
//..
//  assert(6.25 == retotal);
//..
//
///Example 3: Summing Absolute Values
/// - - - - - - - - - - - - - - - - -
// Suppose we have a sequence of numbers and we would like to sum their
// absolute values.  We can use 'bdlb::TransformIterator' for this purpose.
//
// First, we set up the numbers:
//..
//  int data[5] = { 1, -1, 2, -2, 3 };
//..
// Then, we need a functor that will return the absolute value of a number.
// Rather than write a functor object, we can use a simple pointer to function
// as a functor:
//..
//  int (*abs)(int) = &bsl::abs;
//..
// Next, we create the transform iterators that will convert a number to its
// absolute value.  We need iterators for both the beginning and end of the
// sequence:
//..
//  bdlb::TransformIterator<int(*)(int), int *> dataBegin(data + 0, abs);
//  bdlb::TransformIterator<int(*)(int), int *> dataEnd  (data + 5, abs);
//..
// Now, we compute the sum of the absolute values of the numbers:
//..
//  int sum = bsl::accumulate(dataBegin, dataEnd, 0);
//..
// Finally, we verify that we have computed the sum correctly:
//..
//  assert(9 == sum);
//..

#include <bdlscm_version.h>

#include <bslalg_constructorproxy.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_conditional.h>
#include <bslmf_isreference.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_removecv.h>
#include <bslmf_removereference.h>

#include <bsls_compilerfeatures.h>
#include <bsls_libraryfeatures.h>
#include <bsls_util.h>

#include <bsl_algorithm.h>
#include <bsl_functional.h>
#include <bsl_iterator.h>
#include <bsl_utility.h>

#ifndef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE
#include <bslmf_resulttype.h>
#endif

namespace BloombergLP {
namespace bdlb {

// FORWARD DECLARATIONS
template <class FUNCTOR, class ITERATOR>
class TransformIterator;

                      // ===============================
                      // struct TransformIterator_Traits
                      // ===============================

template <class FUNCTOR, class ITERATOR>
struct TransformIterator_Traits {
    // This component-private class defines various types that are used in the
    // implementation of the transform iterator.

    // PUBLIC TYPES

#ifndef BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE
    typedef typename bslmf::ResultType<FUNCTOR>::type ResultType;
        // Define the result type returned by the functor.  This is not
        // necessarily the same type as the dereference of the iterator.  In
        // C++03, the functor must have a 'result_type' type member.  The
        // specializations below transform function pointers to 'bsl::function'
        // so this works for those types as well.
#else
    typedef decltype(bsl::declval<FUNCTOR>()(*bsl::declval<ITERATOR>()))
                                                      ResultType;
        // Define the result type returned by the functor.  This is not
        // necessarily the same type as the dereference of the iterator.  In
        // C++11, the result type can be determined automatically.  Note that
        // various iterations of the language standard might want to instead
        // use 'std::result_of' or 'std::invoke_result' (which have been
        // variously added and then deprecated), but the following works from
        // C++11 onwards.
#endif

    typedef typename bsl::iterator_traits<ITERATOR> BaseIteratorTraits;
        // Define the iterator traits class of the underlying iterator.

    typedef typename bsl::conditional<
        bsl::is_reference<ResultType>::value,
        typename BaseIteratorTraits::iterator_category,
        bsl::input_iterator_tag>::type                  iterator_category;
        // Define the iterator category of the transform iterator.  If the
        // functor returns a reference type, we pass through the iterator
        // category of the underlying iterator, otherwise we use the input
        // iterator tag (because all the other tags require that dereferencing
        // produces a reference).

    typedef typename bsl::remove_cv<
        typename bsl::remove_reference<ResultType>::type>::type  value_type;
    typedef typename BaseIteratorTraits::difference_type difference_type;
    typedef typename bsl::remove_reference<ResultType>::type    *pointer;
    typedef ResultType                                           reference;
        // Define the remaining standard types of the transform iterator.

#if defined(BSLS_LIBRARYFEATURES_STDCPP_LIBCSTD)
// Sun CC workaround: iterators must be derived from 'std::iterator' to work
// with the native std library algorithms.  However, 'std::iterator' is
// deprecated in C++17, so do not rely on derivation unless required, to avoid
// deprecation warnings on modern compilers.
    typedef bsl::iterator<iterator_category,
                          value_type,
                          difference_type,
                          pointer,
                          ResultType>        Iterator;
        // Define the standard iterator specialization that will apply to the
        // transform iterator.
#endif
};

// Specialize the transform iterator traits template for functors that are
// function or function pointer types.  It is sufficient to inherit from the
// version of the traits class that corresponds to a 'bsl::function' of the
// function type parameter.

template <class RESULT, class ARGUMENT, class ITERATOR>
struct TransformIterator_Traits<RESULT (*)(ARGUMENT), ITERATOR>
: public TransformIterator_Traits<bsl::function<RESULT(ARGUMENT)>, ITERATOR> {
};

template <class RESULT, class ARGUMENT, class ITERATOR>
struct TransformIterator_Traits<RESULT(ARGUMENT), ITERATOR>
: public TransformIterator_Traits<bsl::function<RESULT(ARGUMENT)>, ITERATOR> {
};

// The transform iterator uses allocators only if at least one of its iterator
// or its functor do.  Retrieving the allocator of the transform iterator, if
// it exists, therefore can be implemented by querying subobjects.  We will use
// implementation inheritance to supply the transform iterator with an
// allocator method that will exist only when necessary.

             // ==================================================
             // struct TransformIterator_AllocatorOfIteratorMethod
             // ==================================================

template <class BASE_TYPE, bool>
struct TransformIterator_AllocatorOfIteratorMethod {
    // The 'TransformIterator_AllocatorOfIteratorMethod' class template has an
    // allocator method when its boolean template parameter is 'true', which
    // will be made to be the case when the iterator of the transform iterator
    // uses allocators.  The transform iterator type itself is supplied as
    // 'BASE_TYPE'.
};

template <class BASE_TYPE>
struct TransformIterator_AllocatorOfIteratorMethod<BASE_TYPE, true> {
    // ACCESSORS
    bslma::Allocator *allocator() const;
        // Return the allocator used by the underlying iterator of the
        // associated transform iterator to supply memory.  Note that this
        // class must be a base class of the transform iterator.
};

             // =================================================
             // struct TransformIterator_AllocatorOfFunctorMethod
             // =================================================

template <class BASE_TYPE, bool>
struct TransformIterator_AllocatorOfFunctorMethod {
    // The 'TransformIterator_AllocatorOfFunctorMethod' class template has an
    // allocator method when its boolean template parameter is 'true', which
    // will be made to be the case when the iterator of the transform iterator
    // does not use allocators and the functor of the transform iterator uses
    // allocators.  The transform iterator type itself is supplied as
    // 'BASE_TYPE'.
};

template <class BASE_TYPE>
struct TransformIterator_AllocatorOfFunctorMethod<BASE_TYPE, true> {
    // ACCESSORS
    bslma::Allocator *allocator() const;
        // Return the allocator used by the transforming functor of the
        // associated transform iterator to supply memory.  Note that this
        // class must be a base class of the transform iterator.
};

                          // =======================
                          // class TransformIterator
                          // =======================

template <class FUNCTOR, class ITERATOR>
class TransformIterator
: public TransformIterator_AllocatorOfIteratorMethod<
      TransformIterator<FUNCTOR, ITERATOR>,
      bslma::UsesBslmaAllocator<ITERATOR>::value>
, public TransformIterator_AllocatorOfFunctorMethod<
      TransformIterator<FUNCTOR, ITERATOR>,
      !bslma::UsesBslmaAllocator<ITERATOR>::value &&
          bslma::UsesBslmaAllocator<FUNCTOR>::value>
#if defined(BSLS_LIBRARYFEATURES_STDCPP_LIBCSTD)
// Sun CC workaround: iterators must be derived from 'std::iterator' to work
// with the native std library algorithms.  However, 'std::iterator' is
// deprecated in C++17, so do not rely on derivation unless required, to avoid
// deprecation warnings on modern compilers.
, public TransformIterator_Traits<FUNCTOR, ITERATOR>::Iterator
#endif
{
    // The transform iterator class itself.  Its job is to hold a functor and
    // an iterator, pass through all iterator-related operations to the held
    // iterator, and on dereference, call the functor on the result of
    // dereferencing the iterator and return the result of the call instead.

  private:
    // PRIVATE TYPES
    typedef TransformIterator_Traits<FUNCTOR, ITERATOR> Traits;

    // DATA
    bslalg::ConstructorProxy<ITERATOR> d_iterator;  // underlying iterator
    bslalg::ConstructorProxy<FUNCTOR>  d_functor;   // transforming functor

  public:
    // PUBLIC TYPES
    typedef typename Traits::iterator_category iterator_category;
    typedef typename Traits::value_type        value_type;
    typedef typename Traits::difference_type   difference_type;
    typedef typename Traits::pointer           pointer;
    typedef typename Traits::reference         reference;

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION_IF(
        TransformIterator,
        bslma::UsesBslmaAllocator,
        bslma::UsesBslmaAllocator<ITERATOR>::value ||
        bslma::UsesBslmaAllocator<FUNCTOR> ::value)

    // CREATORS
    TransformIterator();
    explicit TransformIterator(bslma::Allocator *basicAllocator);
        // Create a 'TransformIterator' object whose underlying iterator and
        // functor have default values.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    TransformIterator(const ITERATOR&   iterator,
                      FUNCTOR           functor,
                      bslma::Allocator *basicAllocator = 0);
        // Create a 'TransformIterator' object using the specified 'iterator'
        // and 'functor'.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

    TransformIterator(const TransformIterator&  original,
                      bslma::Allocator         *basicAllocator = 0);
        // Create a 'TransformIterator' object having the same value as the
        // specified 'original' object.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    //! ~TransformIterator() = default;
        // Destroy this object.

    // MANIPULATORS
    TransformIterator& operator=(const TransformIterator& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    TransformIterator& operator+=(difference_type offset);
        // Advance the underlying iterator of this object by the specified
        // (signed) 'offset', and return a reference providing modifiable
        // access to this object.  The behavior is undefined if so advancing
        // the underlying iterator is undefined.

    TransformIterator& operator-=(difference_type offset);
        // Regress the underlying iterator of this object by the specified
        // (signed) 'offset', and return a reference providing modifiable
        // access to this object.  The behavior is undefined if so regressing
        // the underlying iterator is undefined.

    TransformIterator& operator++();
        // Increment the underlying iterator of this object, and return a
        // reference providing modifiable access to this object.  The behavior
        // is undefined if incrementing the underlying iterator is undefined.

    TransformIterator& operator--();
        // Decrement the underlying iterator of this object, and return a
        // reference providing modifiable access to this object.  The behavior
        // is undefined if decrementing the underlying iterator is undefined.

    reference operator*();
        // Return the result of applying the functor of this object to the
        // result of dereferencing the underlying iterator.  The behavior is
        // undefined if dereferencing the underlying iterator is undefined.
        // Note that the behavior of this method is equivalent to:
        //..
        //  functor()(*iterator())
        //..

    pointer operator->();
        // Return the address of the result of applying the functor of this
        // object to the result of dereferencing the underlying iterator.  The
        // behavior is undefined if dereferencing the underlying iterator is
        // undefined.  Note that the behavior of this method is equivalent to:
        //..
        //  &functor()(*iterator())
        //..
        // Also note that the functor must return a reference type for this
        // method to be used.

    reference operator[](difference_type offset);
        // Return the result of applying the functor of this object to the
        // result of dereferencing the underlying iterator advanced by the
        // specified (signed) 'offset'.  The behavior is undefined if so
        // advancing or dereferencing the underlying iterator is undefined.
        // Note that the behavior of this method is equivalent to:
        //..
        //  functor()(iterator()[offset])
        //..

    FUNCTOR& functor();
        // Return a reference providing modifiable access to the functor of
        // this object.

    ITERATOR& iterator();
        // Return a reference providing modifiable access to the underlying
        // iterator of this object.

                                  // Aspects

    void swap(TransformIterator& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object by applying 'swap' to each of the functor
        // and underlying iterator fields of the two objects.

    // ACCESSORS
    reference operator*() const;
        // Return the result of applying the functor of this object to the
        // result of dereferencing the underlying iterator.  The behavior is
        // undefined if dereferencing the underlying iterator is undefined.
        // Note that the behavior of this method is equivalent to:
        //..
        //  functor()(*iterator())
        //..

    pointer operator->() const;
        // Return the address of the result of applying the functor of this
        // object to the result of dereferencing the underlying iterator.  The
        // behavior is undefined if dereferencing the underlying iterator is
        // undefined.  Note that the behavior of this method is equivalent to:
        //..
        //  &functor()(*iterator())
        //..
        // Also note that the functor must return a reference type for this
        // method to be used.

    reference operator[](difference_type offset) const;
        // Return the result of applying the functor of this object to the
        // result of dereferencing the underlying iterator advanced by the
        // specified (signed) 'offset'.  The behavior is undefined if so
        // advancing or dereferencing the underlying iterator is undefined.
        // Note that the behavior of this method is equivalent to:
        //..
        //  functor()(iterator()[offset])
        //..

    const FUNCTOR& functor() const;
        // Return a 'const' reference to the functor of this object.

    const ITERATOR& iterator() const;
        // Return a 'const' reference to the underlying iterator of this
        // object.
};

                          // ===========================
                          // class TransformIteratorUtil
                          // ===========================

struct TransformIteratorUtil {
    // This 'struct' provides a namespace for a function template that
    // simplifies the creation of 'TransformIterator' objects by allowing type
    // deduction to discover the types of the functor and underlying iterator.

    // CLASS METHODS
    template <class FUNCTOR, class ITERATOR>
    static TransformIterator<FUNCTOR, ITERATOR> make(
                                         const ITERATOR&   iterator,
                                         const FUNCTOR&    functor,
                                         bslma::Allocator *basicAllocator = 0);
        // Return a 'TransformIterator' object constructed with the specified
        // 'iterator' and 'functor'.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.  Note that if the compiler does
        // not implement the return-value optimization, this function may
        // return a copy created with the default allocator even if a different
        // allocator is supplied.
};

// FREE OPERATORS
template <class FUNCTOR, class ITERATOR>
bool operator==(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                const TransformIterator<FUNCTOR, ITERATOR>& rhs);
    // Return 'true' if the underlying iterator of the specified 'lhs' compares
    // equal to the underlying iterator of the specified 'rhs', and 'false'
    // otherwise.  The behavior is undefined if comparing the underlying
    // iterators in this way is undefined.  Note that the functors are not
    // compared.

template <class FUNCTOR, class ITERATOR>
bool operator!=(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                const TransformIterator<FUNCTOR, ITERATOR>& rhs);
    // Return 'true' if the underlying iterator of the specified 'lhs' compares
    // unequal to the underlying iterator of the specified 'rhs', and 'false'
    // otherwise.  The behavior is undefined if comparing the underlying
    // iterators in this way is undefined.  Note that the functors are not
    // compared.

template <class FUNCTOR, class ITERATOR>
bool operator<(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
               const TransformIterator<FUNCTOR, ITERATOR>& rhs);
    // Return 'true' if the underlying iterator of the specified 'lhs' compares
    // less than the underlying iterator of the specified 'rhs', and 'false'
    // otherwise.  The behavior is undefined if comparing the underlying
    // iterators in this way is undefined.  Note that the functors are not
    // compared.

template <class FUNCTOR, class ITERATOR>
bool operator>(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
               const TransformIterator<FUNCTOR, ITERATOR>& rhs);
    // Return 'true' if the underlying iterator of the specified 'lhs' compares
    // greater than the underlying iterator of the specified 'rhs', and 'false'
    // otherwise.  The behavior is undefined if comparing the underlying
    // iterators in this way is undefined.  Note that the functors are not
    // compared.

template <class FUNCTOR, class ITERATOR>
bool operator<=(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                const TransformIterator<FUNCTOR, ITERATOR>& rhs);
    // Return 'true' if the underlying iterator of the specified 'lhs' compares
    // less than or equal to the underlying iterator of the specified 'rhs',
    // and 'false' otherwise.  The behavior is undefined if comparing the
    // underlying iterators in this way is undefined.  Note that the functors
    // are not compared.

template <class FUNCTOR, class ITERATOR>
bool operator>=(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                const TransformIterator<FUNCTOR, ITERATOR>& rhs);
    // Return 'true' if the underlying iterator of the specified 'lhs' compares
    // greater than or equal to the underlying iterator of the specified 'rhs',
    // and 'false' otherwise.  The behavior is undefined if comparing the
    // underlying iterators in this way is undefined.  Note that the functors
    // are not compared.

template <class FUNCTOR, class ITERATOR>
TransformIterator<FUNCTOR, ITERATOR> operator++(
                                TransformIterator<FUNCTOR, ITERATOR>& iterator,
                                int);
    // Increment the underlying iterator of the specified 'iterator', and
    // return a copy of 'iterator' *before* the increment.  The behavior is
    // undefined if incrementing the underlying iterator is undefined.

template <class FUNCTOR, class ITERATOR>
TransformIterator<FUNCTOR, ITERATOR> operator--(
                                TransformIterator<FUNCTOR, ITERATOR>& iterator,
                                int);
    // Decrement the underlying iterator of the specified 'iterator', and
    // return a copy of 'iterator' *before* the decrement.  The behavior is
    // undefined if decrementing the underlying iterator is undefined.

template <class FUNCTOR, class ITERATOR>
TransformIterator<FUNCTOR, ITERATOR> operator+(
       const TransformIterator<FUNCTOR, ITERATOR>&                    iterator,
       typename TransformIterator<FUNCTOR, ITERATOR>::difference_type offset);
    // Return a copy of the specified 'iterator' object with its underlying
    // iterator advanced by the specified (signed) 'offset' from that of
    // 'iterator'.  The behavior is undefined if so advancing the underlying
    // iterator is undefined.

template <class FUNCTOR, class ITERATOR>
TransformIterator<FUNCTOR, ITERATOR> operator+(
      typename TransformIterator<FUNCTOR, ITERATOR>::difference_type offset,
      const TransformIterator<FUNCTOR, ITERATOR>&                    iterator);
    // Return a copy of the specified 'iterator' object with its underlying
    // iterator advanced by the specified (signed) 'offset' from that of
    // 'iterator'.  The behavior is undefined if so advancing the underlying
    // iterator is undefined.

template <class FUNCTOR, class ITERATOR>
TransformIterator<FUNCTOR, ITERATOR> operator-(
       const TransformIterator<FUNCTOR, ITERATOR>&                    iterator,
       typename TransformIterator<FUNCTOR, ITERATOR>::difference_type offset);
    // Return a copy of the specified 'iterator' object with its underlying
    // iterator regressed by the specified (signed) 'offset' from that of
    // 'iterator'.  The behavior is undefined if so regressing the underlying
    // iterator is undefined.

template <class FUNCTOR, class ITERATOR>
typename TransformIterator<FUNCTOR, ITERATOR>::difference_type operator-(
                                const TransformIterator<FUNCTOR, ITERATOR>& a,
                                const TransformIterator<FUNCTOR, ITERATOR>& b);
    // Return the result of subtracting the underlying iterator of the
    // specified 'a' object from the underlying iterator of the specified 'b'
    // object.  The behavior is undefined if this subtraction is undefined.

// FREE FUNCTIONS
template <class FUNCTOR, class ITERATOR>
void swap(TransformIterator<FUNCTOR, ITERATOR>& a,
          TransformIterator<FUNCTOR, ITERATOR>& b);
    // Efficiently exchange the values of the specified 'a' and 'b' objects by
    // applying 'swap' to each of the functor and underlying iterator fields of
    // the two objects.

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================

             // --------------------------------------------------
             // struct TransformIterator_AllocatorOfIteratorMethod
             // --------------------------------------------------

// ACCESSORS
template <class BASE_TYPE>
inline
bslma::Allocator *TransformIterator_AllocatorOfIteratorMethod<BASE_TYPE, true>
::allocator() const
{
    return static_cast<const BASE_TYPE&>(*this).iterator().allocator();
}

             // -------------------------------------------------
             // struct TransformIterator_AllocatorOfFunctorMethod
             // -------------------------------------------------

// ACCESSORS
template <class BASE_TYPE>
inline
bslma::Allocator *TransformIterator_AllocatorOfFunctorMethod<BASE_TYPE, true>
::allocator() const
{
    return static_cast<const BASE_TYPE&>(*this).functor().allocator();
}

                          //------------------------
                          // class TransformIterator
                          //------------------------

// CREATORS
template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>::TransformIterator()
: d_iterator(0)
, d_functor(0)
{
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>::TransformIterator(
                                              bslma::Allocator *basicAllocator)
: d_iterator(basicAllocator)
, d_functor(basicAllocator)
{
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>::TransformIterator(
                                              const ITERATOR&   iterator,
                                              FUNCTOR           functor,
                                              bslma::Allocator *basicAllocator)
: d_iterator(iterator, basicAllocator)
, d_functor(functor, basicAllocator)
{
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>::TransformIterator(
                                      const TransformIterator&  original,
                                      bslma::Allocator         *basicAllocator)
: d_iterator(original.iterator(), basicAllocator)
, d_functor(original.functor(), basicAllocator)
{
}

// MANIPULATORS
template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>&
TransformIterator<FUNCTOR, ITERATOR>::operator=(const TransformIterator& rhs)
{
    iterator() = rhs.iterator();
    functor()  = rhs.functor();

    return *this;
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>&
TransformIterator<FUNCTOR, ITERATOR>::operator+=(difference_type offset)
{
    iterator() += offset;
    return *this;
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>&
TransformIterator<FUNCTOR, ITERATOR>::operator-=(difference_type offset)
{
    iterator() -= offset;
    return *this;
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>&
TransformIterator<FUNCTOR, ITERATOR>::operator++()
{
    ++iterator();
    return *this;
}

template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR>&
TransformIterator<FUNCTOR, ITERATOR>::operator--()
{
    --iterator();
    return *this;
}

template <class FUNCTOR, class ITERATOR>
inline
typename TransformIterator<FUNCTOR, ITERATOR>::reference
TransformIterator<FUNCTOR, ITERATOR>::operator*()
{
    return functor()(*iterator());
}

template <class FUNCTOR, class ITERATOR>
inline
typename TransformIterator<FUNCTOR, ITERATOR>::pointer
TransformIterator<FUNCTOR, ITERATOR>::operator->()
{
    return bsls::Util::addressOf(functor()(*iterator()));
}

template <class FUNCTOR, class ITERATOR>
inline
typename TransformIterator<FUNCTOR, ITERATOR>::reference
TransformIterator<FUNCTOR, ITERATOR>::operator[](difference_type offset)
{
    return functor()(iterator()[offset]);
}

template <class FUNCTOR, class ITERATOR>
inline
FUNCTOR& TransformIterator<FUNCTOR, ITERATOR>::functor()
{
    return d_functor.object();
}

template <class FUNCTOR, class ITERATOR>
inline
ITERATOR& TransformIterator<FUNCTOR, ITERATOR>::iterator()
{
    return d_iterator.object();
}

                                  // Aspects

template <class FUNCTOR, class ITERATOR>
inline
void TransformIterator<FUNCTOR, ITERATOR>::swap(
                                   TransformIterator<FUNCTOR, ITERATOR>& other)
{
    using bsl::swap;
    swap(functor(), other.functor());
    swap(iterator(), other.iterator());
}

// ACCESSORS
template <class FUNCTOR, class ITERATOR>
inline
typename TransformIterator<FUNCTOR, ITERATOR>::reference
TransformIterator<FUNCTOR, ITERATOR>::operator*() const
{
    return functor()(*iterator());
}

template <class FUNCTOR, class ITERATOR>
inline
typename TransformIterator<FUNCTOR, ITERATOR>::pointer
TransformIterator<FUNCTOR, ITERATOR>::operator->() const
{
    return bsls::Util::addressOf(functor()(*iterator()));
}

template <class FUNCTOR, class ITERATOR>
inline
typename TransformIterator<FUNCTOR, ITERATOR>::reference
TransformIterator<FUNCTOR, ITERATOR>::operator[](difference_type offset) const
{
    return functor()(iterator()[offset]);
}

template <class FUNCTOR, class ITERATOR>
inline
const FUNCTOR& TransformIterator<FUNCTOR, ITERATOR>::functor() const
{
    return d_functor.object();
}

template <class FUNCTOR, class ITERATOR>
inline
const ITERATOR& TransformIterator<FUNCTOR, ITERATOR>::iterator() const
{
    return d_iterator.object();
}

}  // close package namespace

// FREE OPERATORS
template <class FUNCTOR, class ITERATOR>
inline
bool bdlb::operator==(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                      const TransformIterator<FUNCTOR, ITERATOR>& rhs)
{
    return lhs.iterator() == rhs.iterator();
}

template <class FUNCTOR, class ITERATOR>
inline
bool bdlb::operator!=(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                      const TransformIterator<FUNCTOR, ITERATOR>& rhs)
{
    return lhs.iterator() != rhs.iterator();
}

template <class FUNCTOR, class ITERATOR>
inline
bool bdlb::operator<(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                     const TransformIterator<FUNCTOR, ITERATOR>& rhs)
{
    return lhs.iterator() < rhs.iterator();
}

template <class FUNCTOR, class ITERATOR>
inline
bool bdlb::operator>(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                     const TransformIterator<FUNCTOR, ITERATOR>& rhs)
{
    return lhs.iterator() > rhs.iterator();
}

template <class FUNCTOR, class ITERATOR>
inline
bool bdlb::operator<=(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                      const TransformIterator<FUNCTOR, ITERATOR>& rhs)
{
    return lhs.iterator() <= rhs.iterator();
}

template <class FUNCTOR, class ITERATOR>
inline
bool bdlb::operator>=(const TransformIterator<FUNCTOR, ITERATOR>& lhs,
                      const TransformIterator<FUNCTOR, ITERATOR>& rhs)
{
    return lhs.iterator() >= rhs.iterator();
}

template <class FUNCTOR, class ITERATOR>
inline
bdlb::TransformIterator<FUNCTOR, ITERATOR> bdlb::operator++(
                                TransformIterator<FUNCTOR, ITERATOR>& iterator,
                                int)
{
    return TransformIterator<FUNCTOR, ITERATOR>(iterator.iterator()++,
                                                iterator.functor());
}

template <class FUNCTOR, class ITERATOR>
inline
bdlb::TransformIterator<FUNCTOR, ITERATOR> bdlb::operator--(
                                TransformIterator<FUNCTOR, ITERATOR>& iterator,
                                int)
{
    return TransformIterator<FUNCTOR, ITERATOR>(iterator.iterator()--,
                                                iterator.functor());
}

template <class FUNCTOR, class ITERATOR>
inline
bdlb::TransformIterator<FUNCTOR, ITERATOR> bdlb::operator+(
       const TransformIterator<FUNCTOR, ITERATOR>&                    iterator,
       typename TransformIterator<FUNCTOR, ITERATOR>::difference_type offset)
{
    return TransformIterator<FUNCTOR, ITERATOR>(iterator.iterator() + offset,
                                                iterator.functor());
}

template <class FUNCTOR, class ITERATOR>
inline
bdlb::TransformIterator<FUNCTOR, ITERATOR> bdlb::operator+(
       typename TransformIterator<FUNCTOR, ITERATOR>::difference_type offset,
       const TransformIterator<FUNCTOR, ITERATOR>&                    iterator)
{
    return TransformIterator<FUNCTOR, ITERATOR>(iterator.iterator() + offset,
                                                iterator.functor());
}

template <class FUNCTOR, class ITERATOR>
inline
bdlb::TransformIterator<FUNCTOR, ITERATOR> bdlb::operator-(
       const TransformIterator<FUNCTOR, ITERATOR>&                    iterator,
       typename TransformIterator<FUNCTOR, ITERATOR>::difference_type offset)
{
    return TransformIterator<FUNCTOR, ITERATOR>(iterator.iterator() - offset,
                                                iterator.functor());
}

template <class FUNCTOR, class ITERATOR>
inline
typename bdlb::TransformIterator<FUNCTOR, ITERATOR>::difference_type
bdlb::operator-(const TransformIterator<FUNCTOR, ITERATOR>& a,
                const TransformIterator<FUNCTOR, ITERATOR>& b)
{
    return a.iterator() - b.iterator();
}

// FREE FUNCTIONS
template <class FUNCTOR, class ITERATOR>
inline
void bdlb::swap(TransformIterator<FUNCTOR, ITERATOR>& a,
                TransformIterator<FUNCTOR, ITERATOR>& b)
{
    using bsl::swap;
    swap(a.functor(), b.functor());
    swap(a.iterator(), b.iterator());
}

                        // ---------------------------
                        // class TransformIteratorUtil
                        // ---------------------------

namespace bdlb {

// CLASS METHODS
template <class FUNCTOR, class ITERATOR>
inline
TransformIterator<FUNCTOR, ITERATOR> TransformIteratorUtil::make(
                                              const ITERATOR&   iterator,
                                              const FUNCTOR&    functor,
                                              bslma::Allocator *basicAllocator)
{
    return TransformIterator<FUNCTOR, ITERATOR>(
        iterator, functor, basicAllocator);
}

}  // close package namespace
}  // close enterprise namespace

#endif

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