戻り値遅延評価付きファンクタ( + Boost.Range)

TR1でresult内部型(あるいはresult_of)による戻り値型の遅延評価機構の標準化の方向が示されたから,これでますますC++で柔軟かつ気軽ににgeneric programming / functional programmingを実践できる・・・ようになると良いなぁ.標準の関数オブジェクトはもはやテンプレートである必要はなくなるし,operator()をオーバーロードした関数オブジェクトを書けるし,戻り値推論の難しさから実装が難しかった他の便利な関数オブジェクトもばしばし書けるし.これにさらにRange特有のコンポジットのしやすさが組み合わさると,そりゃもう自分の理想の世界が展開されるわけで・・・.問題はresult_ofの普及度がイマイチなことか.そもそもSFINAEと関数型というコンパイラが難儀している標準規格の最たるものを要求してるってのがなぁ.まぁ,Windows, UNIX環境どちらにも標準準拠度がそこそこ高いフリーのコンパイラがあるのが救いだけれど.
以下は何となくこんな感じになればよいなぁと思っているコードの例.VC++7.1とGCC3.3.1(要Boost1.32.0)で動作確認.

#include <boost/mpl/if.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/result_of.hpp>

// 'select1st' extracts the first element of a pair. From SGI STL.
struct select1st
{
  template<class Pair>
  typename Pair::first_type &
  operator()(Pair &p) const
  {
    return p.first;
  }

  template<class Pair>
  typename Pair::first_type const &
  operator()(Pair const &p) const
  {
    return p.first;
  }
}; // struct select1st

// Inject a specialization of 'boost::result_of' for 'select1st'.
// In some cases, 'result' nested type causes cv-qualicfication problems in
// VC++7.1. So I decide to inject specialization of 'boost::result_of' to boost
// namespace instead of 'result' nested type.
namespace boost{

template<class Pair>
struct result_of<select1st (Pair)>
{
  typedef
    typename boost::mpl::if_<
      boost::is_const<Pair>
    , typename Pair::first_type const &
    , typename Pair::first_type &
    >::type
    type
    ;
};

}

// 'select1st' extracts the second element of a pair. From SGI STL.
struct select2nd
{
  template<class Pair>
  typename Pair::second_type &
  operator()(Pair &p) const
  {
    return p.second;
  }

  template<class Pair>
  typename Pair::second_type const &
  operator()(Pair const &p) const
  {
    return p.second;
  }
}; // struct select2nd

namespace boost{

template<class Pair>
struct result_of<select2nd (Pair)>
{
  typedef
    typename boost::mpl::if_<
      boost::is_const<Pair>
    , typename Pair::second_type const &
    , typename Pair::second_type &
    >::type
    type
    ;
};

}





// 'boost::transform_iterator' from Boost.Iterator.
// I hacked it to be able to go with 'boost::result_of'.

// (C) Copyright David Abrahams 2002.
// (C) Copyright Jeremy Siek    2002.
// (C) Copyright Thomas Witt    2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/utility/result_of.hpp>
#include <boost/function.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/detail/enable_if.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/is_reference.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/remove_reference.hpp>

#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1310))
# include <boost/type_traits/is_base_and_derived.hpp>

#endif 
#include <boost/iterator/detail/config_def.hpp>


  template <
    class UnaryFunction, class Iterator
  , class Reference = boost::use_default, class Value = boost::use_default
  >
  class transform_iterator;

  namespace iterator_detail 
  {

    template <class UnaryFunction, class ArgumentType>
    struct function_object_result
    {
      typedef
        typename boost::result_of<UnaryFunction (ArgumentType)>::type
        type
        ;
    };

#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
    //template <class Return, class Argument>
    //struct function_object_result<Return(*)(Argument)>
    //{
    //  typedef Return type;
    //};
#endif

    // Compute the iterator_adaptor instantiation to be used for transform_iterator
    template <class UnaryFunction, class Iterator, class Reference, class Value>
    struct transform_iterator_base
    {
    private:
        // By default, dereferencing the iterator yields the same as
        // the function.  Do we need to adjust the way
        // function_object_result is computed for the standard
        // proposal (e.g. using Doug's result_of)?
      typedef typename boost::detail::ia_dflt_help<
        Reference
      , function_object_result<
          UnaryFunction
        , typename boost::remove_reference<
            typename std::iterator_traits<Iterator>::reference
          >::type
        >
      >::type reference;

        // To get the default for Value: remove any reference on the
        // result type, but retain any constness to signal
        // non-writability.  Note that if we adopt Thomas' suggestion
        // to key non-writability *only* on the Reference argument,
        // we'd need to strip constness here as well.
      typedef typename boost::detail::ia_dflt_help<
        Value
      , boost::remove_reference<reference>
      >::type cv_value_type;

    public:
      typedef boost::iterator_adaptor<
        transform_iterator<UnaryFunction, Iterator, Reference, Value>
      , Iterator
      , cv_value_type
      , boost::use_default    // Leave the traversal category alone
      , reference
      > type;
    };
  }

  template <class UnaryFunction, class Iterator, class Reference, class Value>
  class transform_iterator
    : public iterator_detail::transform_iterator_base<
        UnaryFunction, Iterator, Reference, Value
      >::type
  {
    typedef
      typename iterator_detail::transform_iterator_base<
        UnaryFunction, Iterator, Reference, Value
      >::type
      super_t
      ;

    friend class boost::iterator_core_access;

  public:
    transform_iterator() { }

    transform_iterator(Iterator const& x, UnaryFunction f)
      : super_t(x), m_f(f) { }

    explicit transform_iterator(Iterator const& x)
      : super_t(x)
    {
        // Pro8 is a little too aggressive about instantiating the
        // body of this function.
#if !BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003))
        // don't provide this constructor if UnaryFunction is a
        // function pointer type, since it will be 0.  Too dangerous.
      BOOST_STATIC_ASSERT(boost::is_class<UnaryFunction>::value);
#endif 
    }

    template<
        class OtherUnaryFunction
      , class OtherIterator
      , class OtherReference
      , class OtherValue>
    transform_iterator(
      transform_iterator<OtherUnaryFunction, OtherIterator, OtherReference, OtherValue> const& t
    , typename boost::enable_if_convertible<OtherIterator, Iterator>::type* = 0
#if !BOOST_WORKAROUND(BOOST_MSVC, == 1310)
    , typename boost::enable_if_convertible<OtherUnaryFunction, UnaryFunction>::type* = 0
#endif 
    )
      : super_t(t.base()), m_f(t.functor())
   {}

    UnaryFunction functor() const
      { return m_f; }

  private:
    typename super_t::reference dereference() const
    { return m_f(*this->base()); }

    // Probably should be the initial base class so it can be
    // optimized away via EBO if it is an empty class.
    UnaryFunction m_f;
  };

  template <class UnaryFunction, class Iterator>
  transform_iterator<UnaryFunction, Iterator>
  make_transform_iterator(Iterator it, UnaryFunction fun)
  {
      return transform_iterator<UnaryFunction, Iterator>(it, fun);
  }

  // Version which allows explicit specification of the UnaryFunction
  // type.
  //
  // This generator is not provided if UnaryFunction is a function
  // pointer type, because it's too dangerous: the default-constructed
  // function pointer in the iterator be 0, leading to a runtime
  // crash.
  template <class UnaryFunction, class Iterator>
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
    typename boost::mpl::if_<
#else 
    typename boost::iterators::enable_if<
#endif 
      boost::is_class<UnaryFunction>   // We should probably find a cheaper test than is_class<>
    , transform_iterator<UnaryFunction, Iterator>
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
    , int[3]
#endif 
  >::type
  make_transform_iterator(Iterator it)
  {
      return transform_iterator<UnaryFunction, Iterator>(it, UnaryFunction());
  }

#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) && !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING)
  template <class Return, class Argument, class Iterator>
  transform_iterator< Return (*)(Argument), Iterator, Return>
  make_transform_iterator(Iterator it, Return (*fun)(Argument))
  {
    return transform_iterator<Return (*)(Argument), Iterator, Return>(it, fun);
  }
#endif

#include <boost/iterator/detail/config_undef.hpp>





// Transformed range
#include <boost/range.hpp>
#include <boost/utility/result_of.hpp>

template<class UnaryFunction, class SinglePassRange>
class transformed_range
{
private:
  typedef
    typename boost::range_iterator<SinglePassRange>::type
    range_iterator
    ;
  typedef
    typename boost::range_const_iterator<SinglePassRange>::type
    range_const_iterator
    ;
  typedef
    typename std::iterator_traits<range_iterator>::reference
    base_reference
    ;
  typedef
    typename boost::remove_reference<base_reference>::type
    base_value_type
    ;
public:
  typedef
    typename boost::result_of<UnaryFunction (base_value_type)>::type
    reference
    ;
  typedef
    typename boost::remove_reference<reference>::type
    value_type // Note: cv-qualified to signal the constness.
    ;
  typedef
    transform_iterator<
      UnaryFunction
    , range_iterator
    , reference
    , value_type
    >
    iterator
    ;
  typedef
    transform_iterator<
      UnaryFunction
    , range_const_iterator
    , value_type const &
    , value_type const
    >
    const_iterator
    ;
  typedef
    typename boost::range_difference<SinglePassRange>::type
    difference_type
    ;
  typedef
    typename boost::range_size<SinglePassRange>::type
    size_type
    ;

  transformed_range(SinglePassRange &r)
    : r_(r)
    , f_()
  {
    BOOST_MPL_ASSERT((boost::is_class<UnaryFunction>));
  }

  transformed_range(SinglePassRange &r, UnaryFunction f)
    : r_(r)
    , f_(f)
  { }

  iterator begin()
  {
    return make_transform_iterator(boost::begin(r_), f_);
  }

  const_iterator begin() const
  {
    return make_transform_iterator(boost::const_begin(r_), f_);
  }

  iterator end()
  {
    return make_transform_iterator(boost::end(r_), f_);
  }

  const_iterator end() const
  {
    return make_transform_iterator(boost::const_end(r_), f_);
  }

  size_type size() const
  {
    return boost::size(r_);
  }

  UnaryFunction functor() const
  {
    return f_;
  }

private:
  SinglePassRange &r_;
  UnaryFunction f_;
}; // transformed_range

// Object generators for 'transformed_range'.
template<class UnaryFunction, class SinglePassRange>
inline
transformed_range<UnaryFunction, SinglePassRange>
transformed(SinglePassRange &r, UnaryFunction f)
{
  return transformed_range<UnaryFunction, SinglePassRange>(r, f);
}

template<class UnaryFunction, class SinglePassRange>
inline
transformed_range<UnaryFunction, SinglePassRange const>
transformed(SinglePassRange const &r, UnaryFunction f)
{
  return transformed_range<UnaryFunction, SinglePassRange const>(r, f);
}

template<class UnaryFunction, class SinglePassRange>
inline
transformed_range<UnaryFunction, SinglePassRange>
transformed(SinglePassRange &r)
{
  BOOST_STATIC_ASSERT((boost::is_class<UnaryFunction>::value));
  return transformed_range<UnaryFunction, SinglePassRange>(r);
}

template<class UnaryFunction, class SinglePassRange>
inline
transformed_range<UnaryFunction, SinglePassRange const>
transformed(SinglePassRange const &r)
{
  BOOST_STATIC_ASSERT((boost::is_class<UnaryFunction>::value));
  return transformed_range<UnaryFunction, SinglePassRange const>(r);
}





// Algorithm with range.
template<class SinglePassRange, class OutputIterator>
inline
OutputIterator
copy(SinglePassRange const &r, OutputIterator result)
{
  return std::copy(boost::begin(r), boost::end(r), result);
}





// Now demonstrate.
#include <iostream>
#include <map>

int main()
{
  std::map<int, int> m;
  m.insert(std::make_pair(2, 1));
  m.insert(std::make_pair(3, 2));
  m.insert(std::make_pair(5, 3));
  m.insert(std::make_pair(7, 4));
  m.insert(std::make_pair(11, 5));

  std::ostream_iterator<int> oit(std::cout, "\n");
  copy(transformed<select1st>(m), oit);
  copy(transformed<select2nd>(m), oit);
}