do_the_bind.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*=============================================================================
  2. Copyright (c) 2006-2007 Tobias Schwinger
  3. Use modification and distribution are subject to the Boost Software
  4. License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  5. http://www.boost.org/LICENSE_1_0.txt).
  6. Problem:
  7. How to "do the Bind?"
  8. This recipe shows how to implement a function binder, similar to
  9. Boost.Bind based on the Functional module of Fusion.
  10. It works as follows:
  11. 'bind' is a global, stateless function object. It is implemented in
  12. fused form (fused_binder) and transformed into a variadic function
  13. object. When called, 'bind' returns another function object, which
  14. holds the arguments of the call to 'bind'. It is, again, implemented
  15. in fused form (fused_bound_function) and transformed into unfused
  16. form.
  17. ==============================================================================*/
  18. #include <boost/fusion/functional/invocation/invoke.hpp>
  19. #include <boost/fusion/functional/adapter/unfused.hpp>
  20. #include <boost/fusion/support/deduce_sequence.hpp>
  21. #include <boost/fusion/sequence/intrinsic/at.hpp>
  22. #include <boost/fusion/sequence/intrinsic/front.hpp>
  23. #include <boost/fusion/sequence/intrinsic/size.hpp>
  24. #include <boost/fusion/algorithm/transformation/transform.hpp>
  25. #include <boost/fusion/algorithm/transformation/pop_front.hpp>
  26. #include <boost/fusion/algorithm/iteration/fold.hpp>
  27. #include <boost/fusion/view/filter_view.hpp>
  28. #include <boost/functional/forward_adapter.hpp>
  29. #include <boost/functional/lightweight_forward_adapter.hpp>
  30. #include <boost/type_traits/remove_reference.hpp>
  31. #include <boost/mpl/eval_if.hpp>
  32. #include <boost/mpl/identity.hpp>
  33. #include <boost/mpl/int.hpp>
  34. #include <boost/mpl/max.hpp>
  35. #include <boost/mpl/next.hpp>
  36. #include <boost/ref.hpp>
  37. #include <iostream>
  38. #include <typeinfo>
  39. namespace impl
  40. {
  41. namespace fusion = boost::fusion;
  42. namespace traits = boost::fusion::traits;
  43. namespace result_of = boost::fusion::result_of;
  44. namespace mpl = boost::mpl;
  45. using mpl::placeholders::_;
  46. // Placeholders (we inherit from mpl::int_, so we can use placeholders
  47. // as indices for fusion::at, later)
  48. template <int I> struct placeholder : mpl::int_<I> { };
  49. // A traits class to find out whether T is a placeholeder
  50. template <typename T> struct is_placeholder : mpl::false_ { };
  51. template <int I> struct is_placeholder< placeholder<I> > : mpl::true_ { };
  52. template <int I> struct is_placeholder< placeholder<I> & > : mpl::true_ { };
  53. template <int I> struct is_placeholder< placeholder<I> const > : mpl::true_ { };
  54. template <int I> struct is_placeholder< placeholder<I> const & > : mpl::true_ { };
  55. // This class template provides a Polymorphic Function Object to be used
  56. // with fusion::transform. It is applied to the sequence of arguments that
  57. // describes the binding and holds a reference to the sequence of arguments
  58. // from the final call.
  59. template<class FinalArgs> struct argument_transform
  60. {
  61. FinalArgs const & ref_final_args;
  62. public:
  63. explicit argument_transform(FinalArgs const & final_args)
  64. : ref_final_args(final_args)
  65. { }
  66. // A placeholder? Replace it with an argument from the final call...
  67. template <int Index>
  68. inline typename result_of::at_c<FinalArgs const, Index>::type
  69. operator()(placeholder<Index> const &) const
  70. {
  71. return fusion::at_c<Index>(this->ref_final_args);
  72. }
  73. // ...just return the bound argument, otherwise.
  74. template <typename T> inline T & operator()(T & bound) const
  75. {
  76. return bound;
  77. }
  78. template <typename Signature>
  79. struct result;
  80. template <class Self, typename T>
  81. struct result< Self (T) >
  82. : mpl::eval_if< is_placeholder<T>,
  83. result_of::at<FinalArgs,typename boost::remove_reference<T>::type>,
  84. mpl::identity<T>
  85. >
  86. { };
  87. };
  88. // Fused implementation of the bound function, the function object
  89. // returned by bind
  90. template <class BindArgs> class fused_bound_function
  91. {
  92. // Transform arguments to be held by value
  93. typedef typename traits::deduce_sequence<BindArgs>::type bound_args;
  94. bound_args fsq_bind_args;
  95. public:
  96. fused_bound_function(BindArgs const & bind_args)
  97. : fsq_bind_args(bind_args)
  98. { }
  99. template <typename Signature>
  100. struct result;
  101. template <class FinalArgs>
  102. struct result_impl
  103. : result_of::invoke< typename result_of::front<bound_args>::type,
  104. typename result_of::transform<
  105. typename result_of::pop_front<bound_args>::type,
  106. argument_transform<FinalArgs> const
  107. >::type
  108. >
  109. { };
  110. template <class Self, class FinalArgs>
  111. struct result< Self (FinalArgs) >
  112. : result_impl< typename boost::remove_reference<FinalArgs>::type >
  113. { };
  114. template <class FinalArgs>
  115. inline typename result_impl<FinalArgs>::type
  116. operator()(FinalArgs const & final_args) const
  117. {
  118. return fusion::invoke( fusion::front(this->fsq_bind_args),
  119. fusion::transform( fusion::pop_front(this->fsq_bind_args),
  120. argument_transform<FinalArgs>(final_args) ) );
  121. }
  122. // Could add a non-const variant - omitted for readability
  123. };
  124. // Find the number of placeholders in use
  125. struct n_placeholders
  126. {
  127. struct fold_op
  128. {
  129. template <typename Sig> struct result;
  130. template <class S, class A, class B> struct result< S(A &,B &) >
  131. : mpl::max<A,B> { };
  132. };
  133. struct filter_pred
  134. {
  135. template <class X> struct apply : is_placeholder<X> { };
  136. };
  137. template <typename Seq>
  138. struct apply
  139. : mpl::next< typename result_of::fold<
  140. fusion::filter_view<Seq,filter_pred>, mpl::int_<-1>, fold_op
  141. >::type>::type
  142. { };
  143. };
  144. // Fused implementation of the 'bind' function
  145. struct fused_binder
  146. {
  147. template <class Signature>
  148. struct result;
  149. template <class BindArgs,
  150. int Placeholders = n_placeholders::apply<BindArgs>::value>
  151. struct result_impl
  152. {
  153. typedef boost::forward_adapter<fusion::unfused<
  154. fused_bound_function<BindArgs>,!Placeholders>,Placeholders> type;
  155. };
  156. template <class Self, class BindArgs>
  157. struct result< Self (BindArgs) >
  158. : result_impl< typename boost::remove_reference<BindArgs>::type >
  159. { };
  160. template <class BindArgs>
  161. inline typename result_impl< BindArgs >::type
  162. operator()(BindArgs & bind_args) const
  163. {
  164. return typename result< void(BindArgs) >::type(
  165. fusion::unfused< fused_bound_function<BindArgs>,
  166. ! n_placeholders::apply<BindArgs>::value >(bind_args) );
  167. }
  168. };
  169. // The binder's unfused type. We use lightweght_forward_adapter to make
  170. // that thing more similar to Boost.Bind. Because of that we have to use
  171. // Boost.Ref (below in the sample code)
  172. typedef boost::lightweight_forward_adapter< fusion::unfused<fused_binder> > binder;
  173. }
  174. // Placeholder globals
  175. impl::placeholder<0> const _1_ = impl::placeholder<0>();
  176. impl::placeholder<1> const _2_ = impl::placeholder<1>();
  177. impl::placeholder<2> const _3_ = impl::placeholder<2>();
  178. impl::placeholder<3> const _4_ = impl::placeholder<3>();
  179. // The bind function is a global, too
  180. impl::binder const bind = impl::binder();
  181. // OK, let's try it out:
  182. struct func
  183. {
  184. typedef int result_type;
  185. inline int operator()() const
  186. {
  187. std::cout << "operator()" << std::endl;
  188. return 0;
  189. }
  190. template <typename A>
  191. inline int operator()(A const & a) const
  192. {
  193. std::cout << "operator()(A const & a)" << std::endl;
  194. std::cout << " a = " << a << " A = " << typeid(A).name() << std::endl;
  195. return 1;
  196. }
  197. template <typename A, typename B>
  198. inline int operator()(A const & a, B & b) const
  199. {
  200. std::cout << "operator()(A const & a, B & b)" << std::endl;
  201. std::cout << " a = " << a << " A = " << typeid(A).name() << std::endl;
  202. std::cout << " b = " << b << " B = " << typeid(B).name() << std::endl;
  203. return 2;
  204. }
  205. };
  206. int main()
  207. {
  208. func f;
  209. int value = 42;
  210. using boost::ref;
  211. int errors = 0;
  212. errors += !( bind(f)() == 0);
  213. errors += !( bind(f,"Hi")() == 1);
  214. errors += !( bind(f,_1_)("there.") == 1);
  215. errors += !( bind(f,"The answer is",_1_)(12) == 2);
  216. errors += !( bind(f,_1_,ref(value))("Really?") == 2);
  217. errors += !( bind(f,_1_,_2_)("Dunno. If there is an answer, it's",value) == 2);
  218. return !! errors;
  219. }