external_transforms.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2011 Eric Niebler. Distributed under the Boost
  2. // Software License, Version 1.0. (See accompanying file
  3. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  4. //
  5. // This is an example of how to specify a transform externally so
  6. // that a single grammar can be used to drive multiple differnt
  7. // calculations. In particular, it defines a calculator grammar
  8. // that computes the result of an expression with either checked
  9. // or non-checked division.
  10. #include <iostream>
  11. #include <boost/mpl/int.hpp>
  12. #include <boost/mpl/next.hpp>
  13. #include <boost/mpl/min_max.hpp>
  14. #include <boost/fusion/container/vector.hpp>
  15. #include <boost/fusion/container/generation/make_vector.hpp>
  16. #include <boost/proto/proto.hpp>
  17. #include <boost/test/unit_test.hpp>
  18. namespace mpl = boost::mpl;
  19. namespace proto = boost::proto;
  20. namespace fusion = boost::fusion;
  21. using proto::_;
  22. // The argument placeholder type
  23. template<typename I> struct placeholder : I {};
  24. // Give each rule in the grammar a "name". This is so that we
  25. // can easily dispatch on it later.
  26. struct calc_grammar;
  27. struct divides_rule : proto::divides<calc_grammar, calc_grammar> {};
  28. // Use external transforms in calc_gramar
  29. struct calc_grammar
  30. : proto::or_<
  31. proto::when<
  32. proto::terminal<placeholder<_> >
  33. , proto::functional::at(proto::_state, proto::_value)
  34. >
  35. , proto::when<
  36. proto::terminal<proto::convertible_to<double> >
  37. , proto::_value
  38. >
  39. , proto::when<
  40. proto::plus<calc_grammar, calc_grammar>
  41. , proto::_default<calc_grammar>
  42. >
  43. , proto::when<
  44. proto::minus<calc_grammar, calc_grammar>
  45. , proto::_default<calc_grammar>
  46. >
  47. , proto::when<
  48. proto::multiplies<calc_grammar, calc_grammar>
  49. , proto::_default<calc_grammar>
  50. >
  51. // Note that we don't specify how division nodes are
  52. // handled here. Proto::external_transform is a placeholder
  53. // for an actual transform.
  54. , proto::when<
  55. divides_rule
  56. , proto::external_transform
  57. >
  58. >
  59. {};
  60. template<typename E> struct calc_expr;
  61. struct calc_domain : proto::domain<proto::generator<calc_expr> > {};
  62. template<typename E>
  63. struct calc_expr
  64. : proto::extends<E, calc_expr<E>, calc_domain>
  65. {
  66. calc_expr(E const &e = E()) : calc_expr::proto_extends(e) {}
  67. };
  68. calc_expr<proto::terminal<placeholder<mpl::int_<0> > >::type> _1;
  69. calc_expr<proto::terminal<placeholder<mpl::int_<1> > >::type> _2;
  70. // Use proto::external_transforms to map from named grammar rules to
  71. // transforms.
  72. struct non_checked_division
  73. : proto::external_transforms<
  74. proto::when< divides_rule, proto::_default<calc_grammar> >
  75. >
  76. {};
  77. struct division_by_zero : std::exception {};
  78. struct do_checked_divide
  79. : proto::callable
  80. {
  81. typedef int result_type;
  82. int operator()(int left, int right) const
  83. {
  84. if (right == 0) throw division_by_zero();
  85. return left / right;
  86. }
  87. };
  88. // Use proto::external_transforms again, this time to map the divides_rule
  89. // to a transforms that performs checked division.
  90. struct checked_division
  91. : proto::external_transforms<
  92. proto::when<
  93. divides_rule
  94. , do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right))
  95. >
  96. >
  97. {};
  98. BOOST_PROTO_DEFINE_ENV_VAR(mydata_tag, mydata);
  99. void test_external_transforms()
  100. {
  101. non_checked_division non_checked;
  102. int result1 = calc_grammar()(_1 / _2, fusion::make_vector(6, 2), non_checked);
  103. BOOST_CHECK_EQUAL(result1, 3);
  104. // check that additional data slots are ignored
  105. int result2 = calc_grammar()(_1 / _2, fusion::make_vector(8, 2), (non_checked, mydata = "foo"));
  106. BOOST_CHECK_EQUAL(result2, 4);
  107. // check that we can use the dedicated slot for this purpose
  108. int result3 = calc_grammar()(_1 / _2, fusion::make_vector(8, 2), (42, proto::transforms = non_checked, mydata = "foo"));
  109. BOOST_CHECK_EQUAL(result2, 4);
  110. checked_division checked;
  111. try
  112. {
  113. // This should throw
  114. int result3 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), checked);
  115. BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here!
  116. }
  117. catch(division_by_zero)
  118. {
  119. ; // OK
  120. }
  121. catch(...)
  122. {
  123. BOOST_CHECK(!"Unexpected exception"); // shouldn't get here!
  124. }
  125. try
  126. {
  127. // This should throw
  128. int result4 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), (checked, mydata = test_external_transforms));
  129. BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here!
  130. }
  131. catch(division_by_zero)
  132. {
  133. ; // OK
  134. }
  135. catch(...)
  136. {
  137. BOOST_CHECK(!"Unexpected exception"); // shouldn't get here!
  138. }
  139. try
  140. {
  141. // This should throw
  142. int result5 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), (42, proto::transforms = checked, mydata = test_external_transforms));
  143. BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here!
  144. }
  145. catch(division_by_zero)
  146. {
  147. ; // OK
  148. }
  149. catch(...)
  150. {
  151. BOOST_CHECK(!"Unexpected exception"); // shouldn't get here!
  152. }
  153. }
  154. using namespace boost::unit_test;
  155. ///////////////////////////////////////////////////////////////////////////////
  156. // init_unit_test_suite
  157. //
  158. test_suite* init_unit_test_suite( int argc, char* argv[] )
  159. {
  160. test_suite *test = BOOST_TEST_SUITE("test for external transforms");
  161. test->add(BOOST_TEST_CASE(&test_external_transforms));
  162. return test;
  163. }