virtual_member.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //[ VirtualMember
  2. // Copyright 2008 Eric Niebler. Distributed under the Boost
  3. // Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. //
  6. // This example demonstrates how to use BOOST_PROTO_EXTENDS_MEMBERS()
  7. // to add "virtual" data members to expressions within a domain. For
  8. // instance, with Phoenix you can create a lambda expression such as
  9. //
  10. // if_(_1 > 0)[ std::cout << _2 ].else_[ std::cout << _3 ]
  11. //
  12. // In the above expression, "else_" is a so-called virtual data member
  13. // of the expression "if_(_1 > 0)[ std::cout << _2 ]". This example
  14. // shows how to implement the ".else_" syntax with Proto.
  15. //
  16. // ****WARNING****WARNING****WARNING****WARNING****WARNING****WARNING****
  17. // * The virtual data member feature is experimental and can change at *
  18. // * any time. Use it at your own risk. *
  19. // **********************************************************************
  20. #if defined(_MSC_VER) && _MSC_VER == 1310
  21. #error "Sorry, this example doesn\'t work with MSVC 7.1"
  22. #endif
  23. #include <iostream>
  24. #include <boost/config.hpp>
  25. #include <boost/detail/workaround.hpp>
  26. #include <boost/mpl/eval_if.hpp>
  27. #include <boost/mpl/min_max.hpp>
  28. #include <boost/mpl/next_prior.hpp>
  29. #include <boost/fusion/include/at.hpp>
  30. #include <boost/fusion/include/vector.hpp>
  31. #include <boost/typeof/std/ostream.hpp>
  32. #include <boost/proto/proto.hpp>
  33. namespace mpl = boost::mpl;
  34. namespace proto = boost::proto;
  35. namespace fusion = boost::fusion;
  36. using proto::_;
  37. namespace mini_lambda
  38. {
  39. // A callable PolymorphicFunctionObject that wraps
  40. // fusion::at()
  41. struct at : proto::callable
  42. {
  43. template<class Sig>
  44. struct result;
  45. template<class This, class Vector, class N>
  46. struct result<This(Vector, N)>
  47. : fusion::result_of::at<
  48. typename boost::remove_reference<Vector>::type
  49. , typename boost::remove_reference<N>::type
  50. >
  51. {};
  52. template<class Vector, class N>
  53. typename fusion::result_of::at<Vector const, N>::type
  54. operator()(Vector const &vector, N) const
  55. {
  56. return fusion::at<N>(vector);
  57. }
  58. };
  59. // An MPL IntegralConstant
  60. template<class N>
  61. struct placeholder
  62. {
  63. typedef N type;
  64. typedef typename N::tag tag;
  65. typedef typename N::next next;
  66. typedef typename N::prior prior;
  67. typedef typename N::value_type value_type;
  68. static const value_type value = N::value;
  69. };
  70. // Some keyword types for our lambda EDSL
  71. namespace keyword
  72. {
  73. struct if_ {};
  74. struct else_ {};
  75. struct do_ {};
  76. struct while_ {};
  77. struct try_ {};
  78. struct catch_ {};
  79. }
  80. // Forward declaration for the mini-lambda grammar
  81. struct eval_if_else;
  82. // Forward declaration for the mini-lambda expression wrapper
  83. template<class E>
  84. struct expression;
  85. // The grammar for mini-lambda expressions with transforms for
  86. // evaluating the lambda expression.
  87. struct grammar
  88. : proto::or_<
  89. // When evaluating a placeholder, use the placeholder
  90. // to index into the "data" parameter, which is a fusion
  91. // vector containing the arguments to the lambda expression.
  92. proto::when<
  93. proto::terminal<placeholder<_> >
  94. , at(proto::_data, proto::_value)
  95. >
  96. // When evaluating if/then/else expressions of the form
  97. // "if_( E0 )[ E1 ].else_[ E2 ]", pass E0, E1 and E2 to
  98. // eval_if_else along with the "data" parameter. Note the
  99. // use of proto::member<> to match binary expressions like
  100. // "X.Y" where "Y" is a virtual data member.
  101. , proto::when<
  102. proto::subscript<
  103. proto::member<
  104. proto::subscript<
  105. proto::function<
  106. proto::terminal<keyword::if_>
  107. , grammar
  108. >
  109. , grammar
  110. >
  111. , proto::terminal<keyword::else_>
  112. >
  113. , grammar
  114. >
  115. , eval_if_else(
  116. proto::_right(proto::_left(proto::_left(proto::_left)))
  117. , proto::_right(proto::_left(proto::_left))
  118. , proto::_right
  119. , proto::_data
  120. )
  121. >
  122. , proto::otherwise<
  123. proto::_default<grammar>
  124. >
  125. >
  126. {};
  127. // A callable PolymorphicFunctionObject that evaluates
  128. // if/then/else expressions.
  129. struct eval_if_else : proto::callable
  130. {
  131. typedef void result_type;
  132. template<typename If, typename Then, typename Else, typename Args>
  133. void operator()(If const &if_, Then const &then_, Else const &else_, Args const &args) const
  134. {
  135. if(grammar()(if_, 0, args))
  136. {
  137. grammar()(then_, 0, args);
  138. }
  139. else
  140. {
  141. grammar()(else_, 0, args);
  142. }
  143. }
  144. };
  145. // Define the mini-lambda domain, in which all expressions are
  146. // wrapped in mini_lambda::expression.
  147. struct domain
  148. : proto::domain<proto::pod_generator<expression> >
  149. {};
  150. // A simple transform for computing the arity of
  151. // a lambda expression.
  152. struct arity_of
  153. : proto::or_<
  154. proto::when<
  155. proto::terminal< placeholder<_> >
  156. , mpl::next<proto::_value>()
  157. >
  158. , proto::when<
  159. proto::terminal<_>
  160. , mpl::int_<0>()
  161. >
  162. , proto::otherwise<
  163. proto::fold<
  164. _
  165. , mpl::int_<0>()
  166. , mpl::max<arity_of, proto::_state>()
  167. >
  168. >
  169. >
  170. {};
  171. // Here is the mini-lambda expression wrapper. It serves two purposes:
  172. // 1) To define operator() overloads that evaluate the lambda expression, and
  173. // 2) To define virtual data members like "else_" so that we can write
  174. // expressions like "if_(X)[Y].else_[Z]".
  175. template<class E>
  176. struct expression
  177. {
  178. BOOST_PROTO_BASIC_EXTENDS(E, expression<E>, domain)
  179. BOOST_PROTO_EXTENDS_ASSIGN()
  180. BOOST_PROTO_EXTENDS_SUBSCRIPT()
  181. // Use BOOST_PROTO_EXTENDS_MEMBERS() to define "virtual"
  182. // data members that all expressions in the mini-lambda
  183. // domain will have. They can be used to create expressions
  184. // like "if_(x)[y].else_[z]" and "do_[y].while_(z)".
  185. BOOST_PROTO_EXTENDS_MEMBERS(
  186. ((keyword::else_, else_))
  187. ((keyword::while_, while_))
  188. ((keyword::catch_, catch_))
  189. )
  190. // Calculate the arity of this lambda expression
  191. static int const arity = boost::result_of<arity_of(E)>::type::value;
  192. // Define overloads of operator() that evaluate the lambda
  193. // expression for up to 3 arguments.
  194. // Don't try to compute the return type of the lambda if
  195. // it isn't nullary.
  196. typename mpl::eval_if_c<
  197. 0 != arity
  198. , mpl::identity<void>
  199. , boost::result_of<grammar(
  200. E const &
  201. , int const &
  202. , fusion::vector<> &
  203. )>
  204. >::type
  205. operator()() const
  206. {
  207. BOOST_MPL_ASSERT_RELATION(arity, ==, 0);
  208. fusion::vector<> args;
  209. return grammar()(proto_base(), 0, args);
  210. }
  211. #define BOOST_PROTO_LOCAL_MACRO( \
  212. N, typename_A, A_const_ref, A_const_ref_a, a \
  213. ) \
  214. template<typename_A(N)> \
  215. typename boost::result_of<grammar( \
  216. E const & \
  217. , int const & \
  218. , fusion::vector<A_const_ref(N)> & \
  219. )>::type \
  220. operator ()(A_const_ref_a(N)) const \
  221. { \
  222. BOOST_MPL_ASSERT_RELATION(arity, <=, N); \
  223. fusion::vector<A_const_ref(N)> args(a(N)); \
  224. return grammar()(proto_base(), 0, args); \
  225. }
  226. // Repeats BOOST_PROTO_LOCAL_MACRO macro for N=1 to 3
  227. // inclusive (because there are only 3 placeholders)
  228. #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a
  229. #define BOOST_PROTO_LOCAL_LIMITS (1, 3)
  230. #include BOOST_PROTO_LOCAL_ITERATE()
  231. };
  232. namespace placeholders
  233. {
  234. typedef placeholder<mpl::int_<0> > _1_t;
  235. typedef placeholder<mpl::int_<1> > _2_t;
  236. typedef placeholder<mpl::int_<2> > _3_t;
  237. // Define some placeholders
  238. expression<proto::terminal<_1_t>::type> const _1 = {{{}}};
  239. expression<proto::terminal<_2_t>::type> const _2 = {{{}}};
  240. expression<proto::terminal<_3_t>::type> const _3 = {{{}}};
  241. // Define the if_() statement
  242. template<typename E>
  243. typename proto::result_of::make_expr<proto::tag::function, domain
  244. , keyword::if_
  245. , E const &
  246. >::type const
  247. if_(E const &e)
  248. {
  249. return proto::make_expr<proto::tag::function, domain>(
  250. keyword::if_()
  251. , boost::ref(e)
  252. );
  253. }
  254. }
  255. using placeholders::if_;
  256. }
  257. int main()
  258. {
  259. using namespace mini_lambda::placeholders;
  260. // OK, we can create if/then/else lambda expressions
  261. // and evaluate them.
  262. if_(_1 > 0)
  263. [
  264. std::cout << _2 << '\n'
  265. ]
  266. .else_
  267. [
  268. std::cout << _3 << '\n'
  269. ]
  270. (-42, "positive", "non-positive");
  271. // Even though all expressions in the mini-lambda
  272. // domain have members named else_, while_, and catch_,
  273. // they all occupy the same byte in the expression.
  274. BOOST_MPL_ASSERT_RELATION(sizeof(_1), ==, 2);
  275. return 0;
  276. }
  277. //]