back_end.qbk 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853
  1. [/
  2. / Copyright (c) 2007 Eric Niebler
  3. /
  4. / Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. /]
  7. [/================================================================================]
  8. [section:back_end Back Ends:
  9. Making Expression Templates Do Useful Work]
  10. [/================================================================================]
  11. Now that you've written the front end for your EDSL compiler, and you've learned a bit about the intermediate form it produces, it's time to think about what to /do/ with the intermediate form. This is where you put your domain-specific algorithms and optimizations. Proto gives you two ways to evaluate and manipulate expression templates: contexts and transforms.
  12. * A /context/ is like a function object that you pass along with an expression to
  13. the _eval_ function. It associates behaviors with node types. _eval_ walks the
  14. expression and invokes your context at each node.
  15. * A /transform/ is a way to associate behaviors, not with node types in an
  16. expression, but with rules in a Proto grammar. In this way, they are like
  17. semantic actions in other compiler-construction toolkits.
  18. Two ways to evaluate expressions! How to choose? Since contexts are largely procedural, they are a bit simpler to understand and debug so they are a good place to start. But although transforms are more advanced, they are also more powerful; since they are associated with rules in your grammar, you can select the proper transform based on the entire /structure/ of a sub-expression rather than simply on the type of its top-most node.
  19. Also, transforms have a concise and declarative syntax that can be confusing at first, but highly expressive and fungible once you become accustomed to it. And -- this is admittedly very subjective -- the author finds programming with Proto transforms to be an inordinate amount of /fun!/ Your mileage may vary.
  20. [/================================================================================]
  21. [section:expression_evaluation Expression Evaluation:
  22. Imparting Behaviors with a Context]
  23. [/================================================================================]
  24. Once you have constructed a Proto expression tree, either by using Proto's
  25. operator overloads or with _make_expr_ and friends, you probably want to
  26. actually /do/ something with it. The simplest option is to use `proto::eval()`,
  27. a generic expression evaluator. To use _eval_, you'll need to define a
  28. /context/ that tells _eval_ how each node should be evaluated. This section
  29. goes through the nuts and bolts of using _eval_, defining evaluation contexts,
  30. and using the contexts that Proto provides.
  31. [note `proto::eval()` is a less powerful but easier-to-use evaluation technique
  32. than Proto transforms, which are covered later. Although very powerful,
  33. transforms have a steep learning curve and can be more difficult to debug.
  34. `proto::eval()` is a rather weak tree traversal algorithm. Dan Marsden has
  35. been working on a more general and powerful tree traversal library. When it is
  36. ready, I anticipate that it will eliminate the need for `proto::eval()`.]
  37. [/================================================================]
  38. [section:proto_eval Evaluating an Expression with [^proto::eval()]]
  39. [/================================================================]
  40. [:[*Synopsis:]]
  41. namespace proto
  42. {
  43. namespace result_of
  44. {
  45. // A metafunction for calculating the return
  46. // type of proto::eval() given certain Expr
  47. // and Context types.
  48. template<typename Expr, typename Context>
  49. struct eval
  50. {
  51. typedef
  52. typename Context::template eval<Expr>::result_type
  53. type;
  54. };
  55. }
  56. namespace functional
  57. {
  58. // A callable function object type for evaluating
  59. // a Proto expression with a certain context.
  60. struct eval : callable
  61. {
  62. template<typename Sig>
  63. struct result;
  64. template<typename Expr, typename Context>
  65. typename proto::result_of::eval<Expr, Context>::type
  66. operator ()(Expr &expr, Context &context) const;
  67. template<typename Expr, typename Context>
  68. typename proto::result_of::eval<Expr, Context>::type
  69. operator ()(Expr &expr, Context const &context) const;
  70. };
  71. }
  72. template<typename Expr, typename Context>
  73. typename proto::result_of::eval<Expr, Context>::type
  74. eval(Expr &expr, Context &context);
  75. template<typename Expr, typename Context>
  76. typename proto::result_of::eval<Expr, Context>::type
  77. eval(Expr &expr, Context const &context);
  78. }
  79. Given an expression and an evaluation context, using _eval_ is quite simple.
  80. Simply pass the expression and the context to _eval_ and it does the rest and
  81. returns the result. You can use the `eval<>` metafunction in the
  82. `proto::result_of` namespace to compute the return type of _eval_. The
  83. following demonstrates a use of _eval_:
  84. template<typename Expr>
  85. typename proto::result_of::eval<Expr const, MyContext>::type
  86. MyEvaluate(Expr const &expr)
  87. {
  88. // Some user-defined context type
  89. MyContext ctx;
  90. // Evaluate an expression with the context
  91. return proto::eval(expr, ctx);
  92. }
  93. What _eval_ does is also very simple. It defers most of the work to the
  94. context itself. Here essentially is the implementation of _eval_:
  95. // eval() dispatches to a nested "eval<>" function
  96. // object within the Context:
  97. template<typename Expr, typename Context>
  98. typename Context::template eval<Expr>::result_type
  99. eval(Expr &expr, Context &ctx)
  100. {
  101. typename Context::template eval<Expr> eval_fun;
  102. return eval_fun(expr, ctx);
  103. }
  104. Really, _eval_ is nothing more than a thin wrapper that dispatches to the
  105. appropriate handler within the context class. In the next section, we'll see
  106. how to implement a context class from scratch.
  107. [endsect]
  108. [/==============================================]
  109. [section:contexts Defining an Evaluation Context]
  110. [/==============================================]
  111. As we saw in the previous section, there is really not much to the _eval_
  112. function. Rather, all the interesting expression evaluation goes on within
  113. a context class. This section shows how to implement one from scratch.
  114. All context classes have roughly the following form:
  115. // A prototypical user-defined context.
  116. struct MyContext
  117. {
  118. // A nested eval<> class template
  119. template<
  120. typename Expr
  121. , typename Tag = typename proto::tag_of<Expr>::type
  122. >
  123. struct eval;
  124. // Handle terminal nodes here...
  125. template<typename Expr>
  126. struct eval<Expr, proto::tag::terminal>
  127. {
  128. // Must have a nested result_type typedef.
  129. typedef ... result_type;
  130. // Must have a function call operator that takes
  131. // an expression and the context.
  132. result_type operator()(Expr &expr, MyContext &ctx) const
  133. {
  134. return ...;
  135. }
  136. };
  137. // ... other specializations of struct eval<> ...
  138. };
  139. Context classes are nothing more than a collection of specializations of a
  140. nested `eval<>` class template. Each specialization handles a different
  141. expression type.
  142. In the [link boost_proto.users_guide.getting_started.hello_calculator Hello Calculator]
  143. section, we saw an example of a user-defined context class for evaluating
  144. calculator expressions. That context class was implemented with the help
  145. of Proto's _callable_context_. If we were to implement it from scratch, it
  146. would look something like this:
  147. // The calculator_context from the "Hello Calculator" section,
  148. // implemented from scratch.
  149. struct calculator_context
  150. {
  151. // The values with which we'll replace the placeholders
  152. std::vector<double> args;
  153. template<
  154. typename Expr
  155. // defaulted template parameters, so we can
  156. // specialize on the expressions that need
  157. // special handling.
  158. , typename Tag = typename proto::tag_of<Expr>::type
  159. , typename Arg0 = typename proto::child_c<Expr, 0>::type
  160. >
  161. struct eval;
  162. // Handle placeholder terminals here...
  163. template<typename Expr, int I>
  164. struct eval<Expr, proto::tag::terminal, placeholder<I> >
  165. {
  166. typedef double result_type;
  167. result_type operator()(Expr &, MyContext &ctx) const
  168. {
  169. return ctx.args[I];
  170. }
  171. };
  172. // Handle other terminals here...
  173. template<typename Expr, typename Arg0>
  174. struct eval<Expr, proto::tag::terminal, Arg0>
  175. {
  176. typedef double result_type;
  177. result_type operator()(Expr &expr, MyContext &) const
  178. {
  179. return proto::child(expr);
  180. }
  181. };
  182. // Handle addition here...
  183. template<typename Expr, typename Arg0>
  184. struct eval<Expr, proto::tag::plus, Arg0>
  185. {
  186. typedef double result_type;
  187. result_type operator()(Expr &expr, MyContext &ctx) const
  188. {
  189. return proto::eval(proto::left(expr), ctx)
  190. + proto::eval(proto::right(expr), ctx);
  191. }
  192. };
  193. // ... other eval<> specializations for other node types ...
  194. };
  195. Now we can use _eval_ with the context class above to evaluate calculator
  196. expressions as follows:
  197. // Evaluate an expression with a calculator_context
  198. calculator_context ctx;
  199. ctx.args.push_back(5);
  200. ctx.args.push_back(6);
  201. double d = proto::eval(_1 + _2, ctx);
  202. assert(11 == d);
  203. Defining a context from scratch this way is tedious and verbose, but it gives
  204. you complete control over how the expression is evaluated. The context class in
  205. the [link boost_proto.users_guide.getting_started.hello_calculator Hello Calculator] example
  206. was much simpler. In the next section we'll see the helper class Proto provides
  207. to ease the job of implementing context classes.
  208. [endsect]
  209. [/================================================]
  210. [section:canned_contexts Proto's Built-In Contexts]
  211. [/================================================]
  212. Proto provides some ready-made context classes that you can use as-is, or that
  213. you can use to help while implementing your own contexts. They are:
  214. [variablelist
  215. [ [[link boost_proto.users_guide.back_end.expression_evaluation.canned_contexts.default_context [^default_context]]]
  216. [An evaluation context that assigns the usual C++ meanings to all the
  217. operators. For example, addition nodes are handled by evaluating the
  218. left and right children and then adding the results. The _default_context_
  219. uses Boost.Typeof to deduce the types of the expressions it evaluates.] ]
  220. [ [[link boost_proto.users_guide.back_end.expression_evaluation.canned_contexts.null_context [^null_context]]]
  221. [A simple context that recursively evaluates children but does not combine
  222. the results in any way and returns void.] ]
  223. [ [[link boost_proto.users_guide.back_end.expression_evaluation.canned_contexts.callable_context [^callable_context<>]]]
  224. [A helper that simplifies the job of writing context classes. Rather than
  225. writing template specializations, with _callable_context_ you write a
  226. function object with an overloaded function call operator. Any expressions
  227. not handled by an overload are automatically dispatched to a default
  228. evaluation context that you can specify.] ]
  229. ]
  230. [/=========================================]
  231. [section:default_context [^default_context]]
  232. [/=========================================]
  233. The _default_context_ is an evaluation context that assigns the usual C++
  234. meanings to all the operators. For example, addition nodes are handled by
  235. evaluating the left and right children and then adding the results. The
  236. _default_context_ uses Boost.Typeof to deduce the types of the expressions it
  237. evaluates.
  238. For example, consider the following "Hello World" example:
  239. #include <iostream>
  240. #include <boost/proto/proto.hpp>
  241. #include <boost/proto/context.hpp>
  242. #include <boost/typeof/std/ostream.hpp>
  243. using namespace boost;
  244. proto::terminal< std::ostream & >::type cout_ = { std::cout };
  245. template< typename Expr >
  246. void evaluate( Expr const & expr )
  247. {
  248. // Evaluate the expression with default_context,
  249. // to give the operators their C++ meanings:
  250. proto::default_context ctx;
  251. proto::eval(expr, ctx);
  252. }
  253. int main()
  254. {
  255. evaluate( cout_ << "hello" << ',' << " world" );
  256. return 0;
  257. }
  258. This program outputs the following:
  259. [pre
  260. hello, world
  261. ]
  262. _default_context_ is trivially defined in terms of a `default_eval<>`
  263. template, as follows:
  264. // Definition of default_context
  265. struct default_context
  266. {
  267. template<typename Expr>
  268. struct eval
  269. : default_eval<
  270. Expr
  271. , default_context const
  272. , typename tag_of<Expr>::type
  273. >
  274. {};
  275. };
  276. There are a bunch of `default_eval<>` specializations, each of which handles
  277. a different C++ operator. Here, for instance, is the specialization for binary
  278. addition:
  279. // A default expression evaluator for binary addition
  280. template<typename Expr, typename Context>
  281. struct default_eval<Expr, Context, proto::tag::plus>
  282. {
  283. private:
  284. static Expr & s_expr;
  285. static Context & s_ctx;
  286. public:
  287. typedef
  288. decltype(
  289. proto::eval(proto::child_c<0>(s_expr), s_ctx)
  290. + proto::eval(proto::child_c<1>(s_expr), s_ctx)
  291. )
  292. result_type;
  293. result_type operator ()(Expr &expr, Context &ctx) const
  294. {
  295. return proto::eval(proto::child_c<0>(expr), ctx)
  296. + proto::eval(proto::child_c<1>(expr), ctx);
  297. }
  298. };
  299. The above code uses `decltype` to calculate the return type of the function
  300. call operator. `decltype` is a new keyword in the next version of C++ that gets
  301. the type of any expression. Most compilers do not yet support `decltype`
  302. directly, so `default_eval<>` uses the Boost.Typeof library to emulate it. On
  303. some compilers, that may mean that `default_context` either doesn't work or
  304. that it requires you to register your types with the Boost.Typeof library.
  305. Check the documentation for Boost.Typeof to see.
  306. [endsect]
  307. [/===================================]
  308. [section:null_context [^null_context]]
  309. [/===================================]
  310. The _null_context_ is a simple context that recursively evaluates children
  311. but does not combine the results in any way and returns void. It is useful
  312. in conjunction with `callable_context<>`, or when defining your own contexts
  313. which mutate an expression tree in-place rather than accumulate a result, as
  314. we'll see below.
  315. _null_context_ is trivially implemented in terms of `null_eval<>` as follows:
  316. // Definition of null_context
  317. struct null_context
  318. {
  319. template<typename Expr>
  320. struct eval
  321. : null_eval<Expr, null_context const, Expr::proto_arity::value>
  322. {};
  323. };
  324. And `null_eval<>` is also trivially implemented. Here, for instance is
  325. a binary `null_eval<>`:
  326. // Binary null_eval<>
  327. template<typename Expr, typename Context>
  328. struct null_eval<Expr, Context, 2>
  329. {
  330. typedef void result_type;
  331. void operator()(Expr &expr, Context &ctx) const
  332. {
  333. proto::eval(proto::child_c<0>(expr), ctx);
  334. proto::eval(proto::child_c<1>(expr), ctx);
  335. }
  336. };
  337. When would such classes be useful? Imagine you have an expression tree with
  338. integer terminals, and you would like to increment each integer in-place. You
  339. might define an evaluation context as follows:
  340. struct increment_ints
  341. {
  342. // By default, just evaluate all children by delegating
  343. // to the null_eval<>
  344. template<typename Expr, typename Arg = proto::result_of::child<Expr>::type>
  345. struct eval
  346. : null_eval<Expr, increment_ints const>
  347. {};
  348. // Increment integer terminals
  349. template<typename Expr>
  350. struct eval<Expr, int>
  351. {
  352. typedef void result_type;
  353. void operator()(Expr &expr, increment_ints const &) const
  354. {
  355. ++proto::child(expr);
  356. }
  357. };
  358. };
  359. In the next section on _callable_context_, we'll see an even simpler way to
  360. achieve the same thing.
  361. [endsect]
  362. [/=============================================]
  363. [section:callable_context [^callable_context<>]]
  364. [/=============================================]
  365. The _callable_context_ is a helper that simplifies the job of writing context
  366. classes. Rather than writing template specializations, with _callable_context_
  367. you write a function object with an overloaded function call operator. Any
  368. expressions not handled by an overload are automatically dispatched to a
  369. default evaluation context that you can specify.
  370. Rather than an evaluation context in its own right, _callable_context_ is more
  371. properly thought of as a context adaptor. To use it, you must define your own
  372. context that inherits from _callable_context_.
  373. In the [link boost_proto.users_guide.back_end.expression_evaluation.canned_contexts.null_context [^null_context]]
  374. section, we saw how to implement an evaluation context that increments all the
  375. integers within an expression tree. Here is how to do the same thing with the
  376. _callable_context_:
  377. // An evaluation context that increments all
  378. // integer terminals in-place.
  379. struct increment_ints
  380. : callable_context<
  381. increment_ints const // derived context
  382. , null_context const // fall-back context
  383. >
  384. {
  385. typedef void result_type;
  386. // Handle int terminals here:
  387. void operator()(proto::tag::terminal, int &i) const
  388. {
  389. ++i;
  390. }
  391. };
  392. With such a context, we can do the following:
  393. literal<int> i = 0, j = 10;
  394. proto::eval( i - j * 3.14, increment_ints() );
  395. std::cout << "i = " << i.get() << std::endl;
  396. std::cout << "j = " << j.get() << std::endl;
  397. This program outputs the following, which shows that the integers `i` and `j`
  398. have been incremented by `1`:
  399. [pre
  400. i = 1
  401. j = 11
  402. ]
  403. In the `increment_ints` context, we didn't have to define any nested `eval<>`
  404. templates. That's because _callable_context_ implements them for us.
  405. _callable_context_ takes two template parameters: the derived context and a
  406. fall-back context. For each node in the expression tree being evaluated,
  407. _callable_context_ checks to see if there is an overloaded `operator()` in the
  408. derived context that accepts it. Given some expression `expr` of type `Expr`,
  409. and a context `ctx`, it attempts to call:
  410. ctx(
  411. typename Expr::proto_tag()
  412. , proto::child_c<0>(expr)
  413. , proto::child_c<1>(expr)
  414. ...
  415. );
  416. Using function overloading and metaprogramming tricks, _callable_context_ can
  417. detect at compile-time whether such a function exists or not. If so, that
  418. function is called. If not, the current expression is passed to the fall-back
  419. evaluation context to be processed.
  420. We saw another example of the _callable_context_ when we looked at the simple
  421. calculator expression evaluator. There, we wanted to customize the evaluation
  422. of placeholder terminals, and delegate the handling of all other nodes to the
  423. _default_context_. We did that as follows:
  424. // An evaluation context for calculator expressions that
  425. // explicitly handles placeholder terminals, but defers the
  426. // processing of all other nodes to the default_context.
  427. struct calculator_context
  428. : proto::callable_context< calculator_context const >
  429. {
  430. std::vector<double> args;
  431. // Define the result type of the calculator.
  432. typedef double result_type;
  433. // Handle the placeholders:
  434. template<int I>
  435. double operator()(proto::tag::terminal, placeholder<I>) const
  436. {
  437. return this->args[I];
  438. }
  439. };
  440. In this case, we didn't specify a fall-back context. In that case,
  441. _callable_context_ uses the _default_context_. With the above
  442. `calculator_context` and a couple of appropriately defined placeholder
  443. terminals, we can evaluate calculator expressions, as demonstrated
  444. below:
  445. template<int I>
  446. struct placeholder
  447. {};
  448. terminal<placeholder<0> >::type const _1 = {{}};
  449. terminal<placeholder<1> >::type const _2 = {{}};
  450. // ...
  451. calculator_context ctx;
  452. ctx.args.push_back(4);
  453. ctx.args.push_back(5);
  454. double j = proto::eval( (_2 - _1) / _2 * 100, ctx );
  455. std::cout << "j = " << j << std::endl;
  456. The above code displays the following:
  457. [pre
  458. j = 20
  459. ]
  460. [endsect]
  461. [endsect]
  462. [endsect]
  463. [import ../test/examples.cpp]
  464. [/============================================================================]
  465. [section:expression_transformation Expression Transformation: Semantic Actions]
  466. [/============================================================================]
  467. If you have ever built a parser with the help of a tool like Antlr, yacc or Boost.Spirit, you might be familiar with /semantic actions/. In addition to allowing you to define the grammar of the language recognized by the parser, these tools let you embed code within your grammar that executes when parts of the grammar participate in a parse. Proto has the equivalent of semantic actions. They are called /transforms/. This section describes how to embed transforms within your Proto grammars, turning your grammars into function objects that can manipulate or evaluate expressions in powerful ways.
  468. Proto transforms are an advanced topic. We'll take it slow, using examples to illustrate the key concepts, starting simple.
  469. [/==================================]
  470. [section ["Activating] Your Grammars]
  471. [/==================================]
  472. The Proto grammars we've seen so far are static. You can check at compile-time to see if an expression type matches a grammar, but that's it. Things get more interesting when you give them runtime behaviors. A grammar with embedded transforms is more than just a static grammar. It is a function object that accepts expressions that match the grammar and does /something/ with them.
  473. Below is a very simple grammar. It matches terminal expressions.
  474. // A simple Proto grammar that matches all terminals
  475. proto::terminal< _ >
  476. Here is the same grammar with a transform that extracts the value from the terminal:
  477. // A simple Proto grammar that matches all terminals
  478. // *and* a function object that extracts the value from
  479. // the terminal
  480. proto::when<
  481. proto::terminal< _ >
  482. , proto::_value // <-- Look, a transform!
  483. >
  484. You can read this as follows: when you match a terminal expression, extract the value. The type `proto::_value` is a so-called transform. Later we'll see what makes it a transform, but for now just think of it as a kind of function object. Note the use of _when_: the first template parameter is the grammar to match and the second is the transform to execute. The result is both a grammar that matches terminal expressions and a function object that accepts terminal expressions and extracts their values.
  485. As with ordinary grammars, we can define an empty struct that inherits from a grammar+transform to give us an easy way to refer back to the thing we're defining, as follows:
  486. // A grammar and a function object, as before
  487. struct Value
  488. : proto::when<
  489. proto::terminal< _ >
  490. , proto::_value
  491. >
  492. {};
  493. // "Value" is a grammar that matches terminal expressions
  494. BOOST_MPL_ASSERT(( proto::matches< proto::terminal<int>::type, Value > ));
  495. // "Value" also defines a function object that accepts terminals
  496. // and extracts their value.
  497. proto::terminal<int>::type answer = {42};
  498. Value get_value;
  499. int i = get_value( answer );
  500. As already mentioned, `Value` is a grammar that matches terminal expressions and a function object that operates on terminal expressions. It would be an error to pass a non-terminal expression to the `Value` function object. This is a general property of grammars with transforms; when using them as function objects, expressions passed to them must match the grammar.
  501. Proto grammars are valid TR1-style function objects. That means you can use `boost::result_of<>` to ask a grammar what its return type will be, given a particular expression type. For instance, we can access the `Value` grammar's return type as follows:
  502. // We can use boost::result_of<> to get the return type
  503. // of a Proto grammar.
  504. typedef
  505. typename boost::result_of<Value(proto::terminal<int>::type)>::type
  506. result_type;
  507. // Check that we got the type we expected
  508. BOOST_MPL_ASSERT(( boost::is_same<result_type, int> ));
  509. [note A grammar with embedded transforms is both a grammar and a function object. Calling these things "grammars with transforms" would get tedious. We could call them something like "active grammars", but as we'll see /every/ grammar that you can define with Proto is "active"; that is, every grammar has some behavior when used as a function object. So we'll continue calling these things plain "grammars". The term "transform" is reserved for the thing that is used as the second parameter to the _when_ template.]
  510. [endsect]
  511. [/=========================================]
  512. [section Handling Alternation and Recursion]
  513. [/=========================================]
  514. Most grammars are a little more complicated than the one in the preceding section. For the sake of illustration, let's define a rather nonsensical grammar that matches any expression and recurses to the leftmost terminal and returns its value. It will demonstrate how two key concepts of Proto grammars -- alternation and recursion -- interact with transforms. The grammar is described below.
  515. // A grammar that matches any expression, and a function object
  516. // that returns the value of the leftmost terminal.
  517. struct LeftmostLeaf
  518. : proto::or_<
  519. // If the expression is a terminal, return its value
  520. proto::when<
  521. proto::terminal< _ >
  522. , proto::_value
  523. >
  524. // Otherwise, it is a non-terminal. Return the result
  525. // of invoking LeftmostLeaf on the 0th (leftmost) child.
  526. , proto::when<
  527. _
  528. , LeftmostLeaf( proto::_child0 )
  529. >
  530. >
  531. {};
  532. // A Proto terminal wrapping std::cout
  533. proto::terminal< std::ostream & >::type cout_ = { std::cout };
  534. // Create an expression and use LeftmostLeaf to extract the
  535. // value of the leftmost terminal, which will be std::cout.
  536. std::ostream & sout = LeftmostLeaf()( cout_ << "the answer: " << 42 << '\n' );
  537. We've seen `proto::or_<>` before. Here it is serving two roles. First, it is a grammar that matches any of its alternate sub-grammars; in this case, either a terminal or a non-terminal. Second, it is also a function object that accepts an expression, finds the alternate sub-grammar that matches the expression, and applies its transform. And since `LeftmostLeaf` inherits from `proto::or_<>`, `LeftmostLeaf` is also both a grammar and a function object.
  538. [def _some_transform_ [~some-transform]]
  539. [note The second alternate uses `proto::_` as its grammar. Recall that `proto::_` is the wildcard grammar that matches any expression. Since alternates in `proto::or_<>` are tried in order, and since the first alternate handles all terminals, the second alternate handles all (and only) non-terminals. Often enough, `proto::when< _, _some_transform_ >` is the last alternate in a grammar, so for improved readability, you could use the equivalent `proto::otherwise< _some_transform_ >`.]
  540. The next section describes this grammar further.
  541. [endsect]
  542. [/==========================]
  543. [section Callable Transforms]
  544. [/==========================]
  545. [def __bold_transform__ [*LeftmostLeaf( proto::_child0 )]]
  546. In the grammar defined in the preceding section, the transform associated with non-terminals is a little strange-looking:
  547. proto::when<
  548. _
  549. , __bold_transform__ // <-- a "callable" transform
  550. >
  551. It has the effect of accepting non-terminal expressions, taking the 0th (leftmost) child and recursively invoking the `LeftmostLeaf` function on it. But `LeftmostLeaf( proto::_child0 )` is actually a /function type/. Literally, it is the type of a function that accepts an object of type `proto::_child0` and returns an object of type `LeftmostLeaf`. So how do we make sense of this transform? Clearly, there is no function that actually has this signature, nor would such a function be useful. The key is in understanding how `proto::when<>` /interprets/ its second template parameter.
  552. When the second template parameter to _when_ is a function type, _when_ interprets the function type as a transform. In this case, `LeftmostLeaf` is treated as the type of a function object to invoke, and `proto::_child0` is treated as a transform. First, `proto::_child0` is applied to the current expression (the non-terminal that matched this alternate sub-grammar), and the result (the 0th child) is passed as an argument to `LeftmostLeaf`.
  553. [note *Transforms are a Domain-Specific Language*
  554. `LeftmostLeaf( proto::_child0 )` /looks/ like an invocation of the `LeftmostLeaf` function object, but it's not, but then it actually is! Why this confusing subterfuge? Function types give us a natural and concise syntax for composing more complicated transforms from simpler ones. The fact that the syntax is suggestive of a function invocation is on purpose. It is an embedded domain-specific language for defining expression transformations. If the subterfuge worked, it may have fooled you into thinking the transform is doing exactly what it actually does! And that's the point.]
  555. The type `LeftmostLeaf( proto::_child0 )` is an example of a /callable transform/. It is a function type that represents a function object to call and its arguments. The types `proto::_child0` and `proto::_value` are /primitive transforms/. They are plain structs, not unlike function objects, from which callable transforms can be composed. There is one other type of transform, /object transforms/, that we'll encounter next.
  556. [endsect]
  557. [/========================]
  558. [section Object Transforms]
  559. [/========================]
  560. The very first transform we looked at simply extracted the value of terminals. Let's do the same thing, but this time we'll promote all ints to longs first. (Please forgive the contrived-ness of the examples so far; they get more interesting later.) Here's the grammar:
  561. // A simple Proto grammar that matches all terminals,
  562. // and a function object that extracts the value from
  563. // the terminal, promoting ints to longs:
  564. struct ValueWithPomote
  565. : proto::or_<
  566. proto::when<
  567. proto::terminal< int >
  568. , long(proto::_value) // <-- an "object" transform
  569. >
  570. , proto::when<
  571. proto::terminal< _ >
  572. , proto::_value
  573. >
  574. >
  575. {};
  576. You can read the above grammar as follows: when you match an int terminal, extract the value from the terminal and use it to initialize a long; otherwise, when you match another kind of terminal, just extract the value. The type `long(proto::_value)` is a so-called /object/ transform. It looks like the creation of a temporary long, but it's really a function type. Just as a callable transform is a function type that represents a function to call and its arguments, an object transforms is a function type that represents an object to construct and the arguments to its constructor.
  577. [/================================================]
  578. [note *Object Transforms vs. Callable Transforms*
  579. When using function types as Proto transforms, they can either represent an object to construct or a function to call. It is similar to "normal" C++ where the syntax `foo("arg")` can either be interpreted as an object to construct or a function to call, depending on whether `foo` is a type or a function. But consider two of the transforms we've seen so far:
  580. ``
  581. LeftmostLeaf(proto::_child0) // <-- a callable transform
  582. long(proto::_value) // <-- an object transform
  583. ``
  584. Proto can't know in general which is which, so it uses a trait, `proto::is_callable<>`, to differentiate. `is_callable< long >::value` is false so `long(proto::_value)` is an object to construct, but `is_callable< LeftmostLeaf >::value` is true so `LeftmostLeaf(proto::_child0)` is a function to call. Later on, we'll see how Proto recognizes a type as "callable".]
  585. [/================================================]
  586. [endsect]
  587. [/================================]
  588. [section Example: Calculator Arity]
  589. [/================================]
  590. Now that we have the basics of Proto transforms down, let's consider a slightly more realistic example. We can use transforms to improve the type-safety of the [link boost_proto.users_guide.getting_started.hello_calculator calculator EDSL]. If you recall, it lets you write infix arithmetic expressions involving argument placeholders like `_1` and `_2` and pass them to STL algorithms as function objects, as follows:
  591. double a1[4] = { 56, 84, 37, 69 };
  592. double a2[4] = { 65, 120, 60, 70 };
  593. double a3[4] = { 0 };
  594. // Use std::transform() and a calculator expression
  595. // to calculate percentages given two input sequences:
  596. std::transform(a1, a1+4, a2, a3, (_2 - _1) / _2 * 100);
  597. This works because we gave calculator expressions an `operator()` that evaluates the expression, replacing the placeholders with the arguments to `operator()`. The overloaded `calculator<>::operator()` looked like this:
  598. // Overload operator() to invoke proto::eval() with
  599. // our calculator_context.
  600. template<typename Expr>
  601. double
  602. calculator<Expr>::operator()(double a1 = 0, double a2 = 0) const
  603. {
  604. calculator_context ctx;
  605. ctx.args.push_back(a1);
  606. ctx.args.push_back(a2);
  607. return proto::eval(*this, ctx);
  608. }
  609. Although this works, it's not ideal because it doesn't warn users if they supply too many or too few arguments to a calculator expression. Consider the following mistakes:
  610. (_1 * _1)(4, 2); // Oops, too many arguments!
  611. (_2 * _2)(42); // Oops, too few arguments!
  612. The expression `_1 * _1` defines a unary calculator expression; it takes one argument and squares it. If we pass more than one argument, the extra arguments will be silently ignored, which might be surprising to users. The next expression, `_2 * _2` defines a binary calculator expression; it takes two arguments, ignores the first and squares the second. If we only pass one argument, the code silently fills in `0.0` for the second argument, which is also probably not what users expect. What can be done?
  613. We can say that the /arity/ of a calculator expression is the number of arguments it expects, and it is equal to the largest placeholder in the expression. So, the arity of `_1 * _1` is one, and the arity of `_2 * _2` is two. We can increase the type-safety of our calculator EDSL by making sure the arity of an expression equals the actual number of arguments supplied. Computing the arity of an expression is simple with the help of Proto transforms.
  614. It's straightforward to describe in words how the arity of an expression should
  615. be calculated. Consider that calculator expressions can be made of `_1`, `_2`, literals, unary expressions and binary expressions. The following table shows the arities for each of these 5 constituents.
  616. [table Calculator Sub-Expression Arities
  617. [[Sub-Expression] [Arity]]
  618. [[Placeholder 1] [`1`]]
  619. [[Placeholder 2] [`2`]]
  620. [[Literal] [`0`]]
  621. [[Unary Expression] [ /arity of the operand/ ]]
  622. [[Binary Expression] [ /max arity of the two operands/ ]]
  623. ]
  624. Using this information, we can write the grammar for calculator expressions and attach transforms for computing the arity of each constituent. The code below computes the expression arity as a compile-time integer, using integral wrappers and metafunctions from the Boost MPL Library. The grammar is described below.
  625. [CalcArity]
  626. When we find a placeholder terminal or a literal, we use an /object transform/ such as `mpl::int_<1>()` to create a (default-constructed) compile-time integer representing the arity of that terminal.
  627. For unary expressions, we use `CalcArity(proto::_child)` which is a /callable transform/ that computes the arity of the expression's child.
  628. The transform for binary expressions has a few new tricks. Let's look more closely:
  629. // Compute the left and right arities and
  630. // take the larger of the two.
  631. mpl::max<CalcArity(proto::_left),
  632. CalcArity(proto::_right)>()
  633. This is an object transform; it default-constructs ... what exactly? The `mpl::max<>` template is an MPL metafunction that accepts two compile-time integers. It has a nested `::type` typedef (not shown) that is the maximum of the two. But here, we appear to be passing it two things that are /not/ compile-time integers; they're Proto callable transforms. Proto is smart enough to recognize that fact. It first evaluates the two nested callable transforms, computing the arities of the left and right child expressions. Then it puts the resulting integers into `mpl::max<>` and evaluates the metafunction by asking for the nested `::type`. That is the type of the object that gets default-constructed and returned.
  634. More generally, when evaluating object transforms, Proto looks at the object type and checks whether it is a template specialization, like `mpl::max<>`. If it is, Proto looks for nested transforms that it can evaluate. After any nested transforms have been evaluated and substituted back into the template, the new template specialization is the result type, unless that type has a nested `::type`, in which case that becomes the result.
  635. Now that we can calculate the arity of a calculator expression, let's redefine the `calculator<>` expression wrapper we wrote in the Getting Started guide to use the `CalcArity` grammar and some macros from Boost.MPL to issue compile-time errors when users specify too many or too few arguments.
  636. // The calculator expression wrapper, as defined in the Hello
  637. // Calculator example in the Getting Started guide. It behaves
  638. // just like the expression it wraps, but with extra operator()
  639. // member functions that evaluate the expression.
  640. // NEW: Use the CalcArity grammar to ensure that the correct
  641. // number of arguments are supplied.
  642. template<typename Expr>
  643. struct calculator
  644. : proto::extends<Expr, calculator<Expr>, calculator_domain>
  645. {
  646. typedef
  647. proto::extends<Expr, calculator<Expr>, calculator_domain>
  648. base_type;
  649. calculator(Expr const &expr = Expr())
  650. : base_type(expr)
  651. {}
  652. typedef double result_type;
  653. // Use CalcArity to compute the arity of Expr:
  654. static int const arity = boost::result_of<CalcArity(Expr)>::type::value;
  655. double operator()() const
  656. {
  657. BOOST_MPL_ASSERT_RELATION(0, ==, arity);
  658. calculator_context ctx;
  659. return proto::eval(*this, ctx);
  660. }
  661. double operator()(double a1) const
  662. {
  663. BOOST_MPL_ASSERT_RELATION(1, ==, arity);
  664. calculator_context ctx;
  665. ctx.args.push_back(a1);
  666. return proto::eval(*this, ctx);
  667. }
  668. double operator()(double a1, double a2) const
  669. {
  670. BOOST_MPL_ASSERT_RELATION(2, ==, arity);
  671. calculator_context ctx;
  672. ctx.args.push_back(a1);
  673. ctx.args.push_back(a2);
  674. return proto::eval(*this, ctx);
  675. }
  676. };
  677. Note the use of `boost::result_of<>` to access the return type of the `CalcArity` function object. Since we used compile-time integers in our transforms, the arity of the expression is encoded in the return type of the `CalcArity` function object. Proto grammars are valid TR1-style function objects, so you can use `boost::result_of<>` to figure out their return types.
  678. With our compile-time assertions in place, when users provide too many or too few arguments to a calculator expression, as in:
  679. (_2 * _2)(42); // Oops, too few arguments!
  680. ... they will get a compile-time error message on the line with the assertion that reads something like this[footnote This error message was generated with Microsoft Visual C++ 9.0. Different compilers will emit different messages with varying degrees of readability.]:
  681. [pre
  682. c:\boost\org\trunk\libs\proto\scratch\main.cpp(97) : error C2664: 'boost::mpl::asse
  683. rtion\_failed' : cannot convert parameter 1 from 'boost::mpl::failed \*\*\*\*\*\*\*\*\*\*\*\*boo
  684. st::mpl::assert\_relation<x,y,\_\_formal>::\*\*\*\*\*\*\*\*\*\*\*\*' to 'boost::mpl::assert<false>
  685. ::type'
  686. with
  687. \[
  688. x\=1,
  689. y\=2,
  690. \_\_formal\=bool boost::mpl::operator\=\=(boost::mpl::failed,boost::mpl::failed)
  691. \]
  692. ]
  693. The point of this exercise was to show that we can write a fairly simple Proto grammar with embedded transforms that is declarative and readable and can compute interesting properties of arbitrarily complicated expressions. But transforms can do more than that. Boost.Xpressive uses transforms to turn expressions into finite state automata for matching regular expressions, and Boost.Spirit uses transforms to build recursive descent parser generators. Proto comes with a collection of built-in transforms that you can use to perform very sophisticated expression manipulations like these. In the next few sections we'll see some of them in action.
  694. [endsect]
  695. [/===============================================]
  696. [section:state Transforms With State Accumulation]
  697. [/===============================================]
  698. So far, we've only seen examples of grammars with transforms that accept one argument: the expression to transform. But consider for a moment how, in ordinary procedural code, you would turn a binary tree into a linked list. You would start with an empty list. Then, you would recursively convert the right branch to a list, and use the result as the initial state while converting the left branch to a list. That is, you would need a function that takes two parameters: the current node and the list so far. These sorts of /accumulation/ problems are quite common when processing trees. The linked list is an example of an accumulation variable or /state/. Each iteration of the algorithm takes the current element and state, applies some binary function to the two and creates a new state. In the STL, this algorithm is called `std::accumulate()`. In many other languages, it is called /fold/. Let's see how to implement a fold algorithm with Proto transforms.
  699. All Proto grammars can optionally accept a state parameter in addition to the expression to transform. If you want to fold a tree to a list, you'll need to make use of the state parameter to pass around the list you've built so far. As for the list, the Boost.Fusion library provides a `fusion::cons<>` type from which you can build heterogeneous lists. The type `fusion::nil` represents an empty list.
  700. Below is a grammar that recognizes output expressions like `cout_ << 42 << '\n'` and puts the arguments into a Fusion list. It is explained below.
  701. // Fold the terminals in output statements like
  702. // "cout_ << 42 << '\n'" into a Fusion cons-list.
  703. struct FoldToList
  704. : proto::or_<
  705. // Don't add the ostream terminal to the list
  706. proto::when<
  707. proto::terminal< std::ostream & >
  708. , proto::_state
  709. >
  710. // Put all other terminals at the head of the
  711. // list that we're building in the "state" parameter
  712. , proto::when<
  713. proto::terminal<_>
  714. , fusion::cons<proto::_value, proto::_state>(
  715. proto::_value, proto::_state
  716. )
  717. >
  718. // For left-shift operations, first fold the right
  719. // child to a list using the current state. Use
  720. // the result as the state parameter when folding
  721. // the left child to a list.
  722. , proto::when<
  723. proto::shift_left<FoldToList, FoldToList>
  724. , FoldToList(
  725. proto::_left
  726. , FoldToList(proto::_right, proto::_state)
  727. )
  728. >
  729. >
  730. {};
  731. Before reading on, see if you can apply what you know already about object, callable and primitive transforms to figure out how this grammar works.
  732. When you use the `FoldToList` function, you'll need to pass two arguments: the expression to fold, and the initial state: an empty list. Those two arguments get passed around to each transform. We learned previously that `proto::_value` is a primitive transform that accepts a terminal expression and extracts its value. What we didn't know until now was that it also accepts the current state /and ignores it/. `proto::_state` is also a primitive transform. It accepts the current expression, which it ignores, and the current state, which it returns.
  733. When we find a terminal, we stick it at the head of the cons list, using the current state as the tail of the list. (The first alternate causes the `ostream` to be skipped. We don't want `cout` in the list.) When we find a shift-left node, we apply the following transform:
  734. // Fold the right child and use the result as
  735. // state while folding the right.
  736. FoldToList(
  737. proto::_left
  738. , FoldToList(proto::_right, proto::_state)
  739. )
  740. You can read this transform as follows: using the current state, fold the right child to a list. Use the new list as the state while folding the left child to a list.
  741. [tip If your compiler is Microsoft Visual C++, you'll find that the above transform does not compile. The compiler has bugs with its handling of nested function types. You can work around the bug by wrapping the inner transform in `proto::call<>` as follows:
  742. ``
  743. FoldToList(
  744. proto::_left
  745. , proto::call<FoldToList(proto::_right, proto::_state)>
  746. )
  747. ``
  748. `proto::call<>` turns a callable transform into a primitive transform, but more on that later.
  749. ]
  750. Now that we have defined the `FoldToList` function object, we can use it to turn output expressions into lists as follows:
  751. proto::terminal<std::ostream &>::type const cout_ = {std::cout};
  752. // This is the type of the list we build below
  753. typedef
  754. fusion::cons<
  755. int
  756. , fusion::cons<
  757. double
  758. , fusion::cons<
  759. char
  760. , fusion::nil
  761. >
  762. >
  763. >
  764. result_type;
  765. // Fold an output expression into a Fusion list, using
  766. // fusion::nil as the initial state of the transformation.
  767. FoldToList to_list;
  768. result_type args = to_list(cout_ << 1 << 3.14 << '\n', fusion::nil());
  769. // Now "args" is the list: {1, 3.14, '\n'}
  770. When writing transforms, "fold" is such a basic operation that Proto provides a number of built-in fold transforms. We'll get to them later. For now, rest assured that you won't always have to stretch your brain so far to do such basic things.
  771. [endsect]
  772. [/================================================]
  773. [section:data Passing Auxiliary Data to Transforms]
  774. [/================================================]
  775. In the last section, we saw that we can pass a second parameter to grammars with transforms: an accumulation variable or /state/ that gets updated as your transform executes. There are times when your transforms will need to access auxiliary data that does /not/ accumulate, so bundling it with the state parameter is impractical. Instead, you can pass auxiliary data as a third parameter, known as the /data/ parameter.
  776. Let's modify our previous example so that it writes each terminal to `std::cout` before it puts it into a list. This could be handy for debugging your transforms, for instance. We can make it general by passing a `std::ostream` into the transform in the data parameter. Within the transform itself, we can retrieve the `ostream` with the _data_pt_ transform. The strategy is as follows: use the _and_ transform to chain two actions. The second action will create the `fusion::cons<>` node as before. The first action, however, will display the current expression. For that, we first construct an instance of [classref boost::proto::functional::display_expr `proto::functional::display_expr`] and then call it.
  777. // Fold the terminals in output statements like
  778. // "cout_ << 42 << '\n'" into a Fusion cons-list.
  779. struct FoldToList
  780. : proto::or_<
  781. // Don't add the ostream terminal to the list
  782. proto::when<
  783. proto::terminal< std::ostream & >
  784. , proto::_state
  785. >
  786. // Put all other terminals at the head of the
  787. // list that we're building in the "state" parameter
  788. , proto::when<
  789. proto::terminal<_>
  790. , proto::and_<
  791. // First, write the terminal to an ostream passed
  792. // in the data parameter
  793. proto::lazy<
  794. proto::make<proto::functional::display_expr(proto::_data)>(_)
  795. >
  796. // Then, constuct the new cons list.
  797. , fusion::cons<proto::_value, proto::_state>(
  798. proto::_value, proto::_state
  799. )
  800. >
  801. >
  802. // For left-shift operations, first fold the right
  803. // child to a list using the current state. Use
  804. // the result as the state parameter when folding
  805. // the left child to a list.
  806. , proto::when<
  807. proto::shift_left<FoldToList, FoldToList>
  808. , FoldToList(
  809. proto::_left
  810. , FoldToList(proto::_right, proto::_state, proto::_data)
  811. , proto::_data
  812. )
  813. >
  814. >
  815. {};
  816. This is a lot to take in, no doubt. But focus on the second `when` clause above. It says: when you find a terminal, first display the terminal using the `ostream` you find in the data parameter, then take the value of the terminal and the current state to build a new `cons` list. The function object `display_expr` does the job of printing the terminal, and `proto::and_<>` chains the actions together and executes them in sequence, returning the result of the last one.
  817. [def __the_expr__ ['the-expr]]
  818. [note Also new is _lazy_pt_. Sometimes you don't have a ready-made callable object to execute. Instead, you want to first make one and /then/ execute it. Above, we need to create a `display_expr`, initializing it with our `ostream`. After that, we want to invoke it by passing it the current expression. It's as if we were doing `display_expr(std::cout)(__the_expr__)`. We achieve this two-phase evaluation using `proto::lazy<>`. If this doesn't make sense yet, don't worry about it.]
  819. We can use the above transform as before, but now we can pass an `ostream` as the third parameter and get to watch the transform in action. Here's a sample usage:
  820. proto::terminal<std::ostream &>::type const cout_ = {std::cout};
  821. // This is the type of the list we build below
  822. typedef
  823. fusion::cons<
  824. int
  825. , fusion::cons<
  826. double
  827. , fusion::cons<
  828. char
  829. , fusion::nil
  830. >
  831. >
  832. >
  833. result_type;
  834. // Fold an output expression into a Fusion list, using
  835. // fusion::nil as the initial state of the transformation.
  836. // Pass std::cout as the data parameter so that we can track
  837. // the progress of the transform on the console.
  838. FoldToList to_list;
  839. result_type args = to_list(cout_ << 1 << 3.14 << '\n', fusion::nil(), std::cout);
  840. // Now "args" is the list: {1, 3.14, '\n'}
  841. This code displays the following:
  842. [pre terminal(
  843. )
  844. terminal(3.14)
  845. terminal(1)]
  846. This is a rather round-about way of demonstrating that you can pass extra data to a transform as a third parameter. There are no restrictions on what this parameter can be, and, unlike the state parameter, Proto will never mess with it.
  847. [heading Transform Environment Variables]
  848. [note ['This is an advanced topic. Feel free to skip if you are new to Proto.]]
  849. The example above uses the data parameter as a transport mechanism for an unstructured blob of data; in this case, a reference to an `ostream`. As your Proto algorithms become more sophisticated, you may find that an unstructured blob of data isn't terribly convenient to work with. Different parts of your algorithm may be interested in different bits of data. What you want, instead, is a way to pass in a collection of /environment variables/ to a transform, like a collection of key\/value pairs. Then, you can easily get at the piece of data you want by asking the data parameter for the value associated with a particular key. Proto's /transform environments/ give you just that.
  850. Let's start by defining a key.
  851. BOOST_PROTO_DEFINE_ENV_VAR(mykey_type, mykey);
  852. This defines a global constant `mykey` with the type `mykey_type`. We can use `mykey` to store a piece of assiciated data in a transform environment, as so:
  853. // Call the MyEval algorithm with a transform environment containing
  854. // two key/value pairs: one for proto::data and one for mykey
  855. MyEval()( expr, state, (proto::data = 42, mykey = "hello world") );
  856. The above means to invoke the `MyEval` algorithm with three parameters: an expression, an initial state, and a transform environment containing two key\/value pairs.
  857. From within a Proto algorithm, you can access the values associated with different keys using the [classref boost::proto::_env_var `proto::_env_var<>`] transform. For instance, `proto::_env_var<mykey_type>` would fetch the value `"hello world"` from the transform environment created above.
  858. The `proto::_data` transform has some additional smarts. Rather than always returning the third parameter regarless of whether it is a blob or a transform environment, it checks first to see if it's a blob or not. If so, that's what gets returned. If not, it returns the value associated with the `proto::data` key. In the above example, that would be the value `42`.
  859. There's a small host of functions, metafunction, and classes that you can use to create and manipulate transform environments, some for testing whether an object is a transform environment, some for coercing an object to be a transform environment, and some for querying a transform environment whether or not is has a value for a particular key. For an exhaustive treatment of the topic, check out the reference for the [headerref boost/proto/transform/env.hpp] header.
  860. [endsect]
  861. [section:implicit_params Implicit Parameters to Primitive Transforms]
  862. Let's use `FoldToList` example from the previous two sections to illustrate some other niceties of Proto transforms. We've seen that grammars, when used as function objects, can accept up to 3 parameters, and that when using these grammars in callable transforms, you can also specify up to 3 parameters. Let's take another look at the transform associated with non-terminals from the last section:
  863. FoldToList(
  864. proto::_left
  865. , FoldToList(proto::_right, proto::_state, proto::_data)
  866. , proto::_data
  867. )
  868. Here we specify all three parameters to both invocations of the `FoldToList` grammar. But we don't have to specify all three. If we don't specify a third parameter, `proto::_data` is assumed. Likewise for the second parameter and `proto::_state`. So the above transform could have been written more simply as:
  869. FoldToList(
  870. proto::_left
  871. , StringCopy(proto::_right)
  872. )
  873. The same is true for any primitive transform. The following are all equivalent:
  874. [table Implicit Parameters to Primitive Transforms
  875. [[Equivalent Transforms]]
  876. [[`proto::when<_, FoldToList>`]]
  877. [[`proto::when<_, FoldToList()>`]]
  878. [[`proto::when<_, FoldToList(_)>`]]
  879. [[`proto::when<_, FoldToList(_, proto::_state)>`]]
  880. [[`proto::when<_, FoldToList(_, proto::_state, proto::_data)>`]]
  881. ]
  882. [note *Grammars Are Primitive Transforms Are Function Objects*
  883. So far, we've said that all Proto grammars are function objects. But it's more accurate to say that Proto grammars are primitive transforms -- a special kind of function object that takes between 1 and 3 arguments, and that Proto knows to treat specially when used in a callable transform, as in the table above.]
  884. [note *Not All Function Objects Are Primitive Transforms*
  885. You might be tempted now to drop the `_state` and `_data` parameters for all your callable transforms. That would be an error. You can only do that for primitive transforms, and not all callables are primitive transforms. Later on, we'll see what distinguishes ordinary callables from their more powerful primitive transfor cousins, but the short version is this: primitive transforms inherit from [classref boost::proto::transform `proto::transform<>`].]
  886. Once you know that primitive transforms will always receive all three parameters -- expression, state, and data -- it makes things possible that wouldn't be otherwise. For instance, consider that for binary expressions, these two transforms are equivalent. Can you see why?
  887. [table Two Equivalent Transforms
  888. [[Without [^proto::reverse_fold<>]][With [^proto::reverse_fold<>]]]
  889. [[``FoldToList(
  890. proto::_left
  891. , FoldToList(proto::_right, proto::_state, proto::_data)
  892. , proto::_data
  893. )``
  894. ][``proto::reverse_fold<_, proto::_state, FoldToList>``]]
  895. ]
  896. [endsect]
  897. [/==================================================]
  898. [section:unpacking_expressions Unpacking Expressions]
  899. [/==================================================]
  900. Processing expressions with an arbitrary number of children can be a pain. What if you want to do something to each child, then pass the results as arguments to some other function? Can you do it just once without worrying about how many children an expression has? Yes. This is where Proto's /unpacking expressions/ come in handy. Unpacking expressions give you a way to write callable and object transforms that handle ['n]-ary expressions.
  901. [note *Inspired by C++11 Variadic Templates*
  902. Proto's unpacking expressions take inspiration from the C++11 feature of the same name. If you are familiar with variadic functions, and in particular how to expand a function parameter pack, this discussion should seem very familiar. However, this feature doesn't actually use any C++11 features, so the code describe here will work with any compliant C++98 compiler.]
  903. [heading Example: A C++ Expression Evaluator]
  904. Proto has the built-in _default_pt_ transform for evaluating Proto expressions in a C++-ish way. But if it didn't, it wouldn't be too hard to implement one from scratch using Proto's unpacking patterns. The transform `eval` below does just that.
  905. // A callable polymorphic function object that takes an unpacked expression
  906. // and a tag, and evaluates the expression. A plus tag and two operands adds
  907. // them with operator +, for instance.
  908. struct do_eval : proto::callable
  909. {
  910. typedef double result_type;
  911. #define UNARY_OP(TAG, OP) \
  912. template<typename Arg> \
  913. double operator()(proto::tag::TAG, Arg arg) const \
  914. { \
  915. return OP arg; \
  916. } \
  917. /**/
  918. #define BINARY_OP(TAG, OP) \
  919. template<typename Left, typename Right> \
  920. double operator()(proto::tag::TAG, Left left, Right right) const \
  921. { \
  922. return left OP right; \
  923. } \
  924. /**/
  925. UNARY_OP(negate, -)
  926. BINARY_OP(plus, +)
  927. BINARY_OP(minus, -)
  928. BINARY_OP(multiplies, *)
  929. BINARY_OP(divides, /)
  930. /*... others ...*/
  931. };
  932. struct eval
  933. : proto::or_<
  934. // Evaluate terminals by simply returning their value
  935. proto::when<proto::terminal<_>, proto::_value>
  936. // Non-terminals are handled by unpacking the expression,
  937. // recursively calling eval on each child, and passing
  938. // the results along with the expression's tag to do_eval
  939. // defined above.
  940. , proto::otherwise<do_eval(proto::tag_of<_>(), eval(proto::pack(_))...)>
  941. // UNPACKING PATTERN HERE -------------------^^^^^^^^^^^^^^^^^^^^^^^^
  942. >
  943. {};
  944. The bulk of the above code is devoted to the `do_eval` function object that maps tag types to behaviors, but the interesting bit is the definition of the `eval` algorithm at the bottom. Terminals are handled quite simply, but non-terminals could be unary, binary, ternary, even ['n]-ary if we consider function call expressions. The `eval` algorithm handles this uniformly with the help of an unpacking pattern.
  945. Non-terminals are evaluated with this callable transform:
  946. do_eval(proto::tag_of<_>(), eval(proto::pack(_))...)
  947. You can read this as: call the `do_eval` function object with the tag of the current expression and all its children after they have each been evaluated with `eval`. The unpacking pattern is the bit just before the ellipsis: `eval(proto::pack(_))`.
  948. What's going on here is this. The unpacking expression gets repeated once for each child in the expression currently being evaluated. In each repetition, the type `proto::pack(_)` gets replaced with [^proto::_child_c<['N]>]. So, if a unary expression is passed to `eval`, it actually gets evaluated like this:
  949. // After the unpacking pattern is expanded for a unary expression
  950. do_eval(proto::tag_of<_>(), eval(proto::_child_c<0>))
  951. And when passed a binary expression, the unpacking pattern expands like this:
  952. // After the unpacking pattern is expanded for a binary expression
  953. do_eval(proto::tag_of<_>(), eval(proto::_child_c<0>), eval(proto::_child_c<1>))
  954. Although it can't happen in our example, when passed a terminal, the unpacking pattern expands such that it extracts the value from the terminal instead of the children. So it gets handled like this:
  955. // If a terminal were passed to this transform, Proto would try
  956. // to evaluate it like this, which would fail:
  957. do_eval(proto::tag_of<_>(), eval(proto::_value))
  958. That doesn't make sense. `proto::_value` would return something that isn't a Proto expression, and `eval` wouldn't be able to evaluate it. Proto algorithms don't work unless you pass them Proto expressions.
  959. [note *Kickin' It Old School*
  960. You may be thinking, my compiler doesn't support C++11 variadic templates! How can this possibly work? The answer is simple: The `...` above isn't a C++11 pack expansion. It's actually an old-school C-style vararg. Remember that callable and object transforms are /function types/. A transform with one of these pseudo-pack expansions is really just the type of a boring, old vararg function. Proto just interprets it differently.]
  961. Unpacking patterns are very expressive. Any callable or object transform can be used as an unpacking pattern, so long as `proto::pack(_)` appears exactly once somewhere within it. This gives you a lot of flexibility in how you want to process the children of an expression before passing them on to some function object or object constructor.
  962. [endsect]
  963. [/=============================================================]
  964. [section:external_transforms Separating Grammars And Transforms]
  965. [/=============================================================]
  966. [note This is an advanced topic that is only necessary for people defining large EDSLs. Feel free to skip this if you're just getting started with Proto.]
  967. So far, we've seen examples of grammars with embedded transforms. In practice, grammars can get pretty large, and you may want to use them to drive several different computations. For instance, you may have a grammar for a linear algebra domain, and you may want to use it to compute the shape of the result (vector or matrix?) and also to compute the result optimally. You don't want to have to copy and paste the whole shebang just to tweak one of the embedded transforms. What you want instead is to define the grammar once, and specify the transforms later when you're ready to evaluate an expression. For that, you use /external transforms/. The pattern you'll use is this: replace one or more of the transforms in your grammar with the special placeholder _external_transform_. Then, you'll create a bundle of transforms that you will pass to the grammar in the data parameter (the 3rd parameter after the expression and state) when evaluating it.
  968. To illustrate external transforms, we'll build a calculator evaluator that can be configured to throw an exception on division by zero. Here is a bare-bones front end that defines a domain, a grammar, an expression wrapper, and some placeholder terminals.
  969. #include <boost/assert.hpp>
  970. #include <boost/mpl/int.hpp>
  971. #include <boost/fusion/container/vector.hpp>
  972. #include <boost/fusion/container/generation/make_vector.hpp>
  973. #include <boost/proto/proto.hpp>
  974. namespace mpl = boost::mpl;
  975. namespace proto = boost::proto;
  976. namespace fusion = boost::fusion;
  977. // The argument placeholder type
  978. template<typename I> struct placeholder : I {};
  979. // The grammar for valid calculator expressions
  980. struct calc_grammar
  981. : proto::or_<
  982. proto::terminal<placeholder<proto::_> >
  983. , proto::terminal<int>
  984. , proto::plus<calc_grammar, calc_grammar>
  985. , proto::minus<calc_grammar, calc_grammar>
  986. , proto::multiplies<calc_grammar, calc_grammar>
  987. , proto::divides<calc_grammar, calc_grammar>
  988. >
  989. {};
  990. template<typename E> struct calc_expr;
  991. struct calc_domain : proto::domain<proto::generator<calc_expr> > {};
  992. template<typename E>
  993. struct calc_expr
  994. : proto::extends<E, calc_expr<E>, calc_domain>
  995. {
  996. calc_expr(E const &e = E()) : calc_expr::proto_extends(e) {}
  997. };
  998. calc_expr<proto::terminal<placeholder<mpl::int_<0> > >::type> _1;
  999. calc_expr<proto::terminal<placeholder<mpl::int_<1> > >::type> _2;
  1000. int main()
  1001. {
  1002. // Build a calculator expression, and do nothing with it.
  1003. (_1 + _2);
  1004. }
  1005. Now, let's embed transforms into `calc_grammar` so that we can use it to evaluate calculator expressions:
  1006. // The calculator grammar with embedded transforms for evaluating expression.
  1007. struct calc_grammar
  1008. : proto::or_<
  1009. proto::when<
  1010. proto::terminal<placeholder<proto::_> >
  1011. , proto::functional::at(proto::_state, proto::_value)
  1012. >
  1013. , proto::when<
  1014. proto::terminal<int>
  1015. , proto::_value
  1016. >
  1017. , proto::when<
  1018. proto::plus<calc_grammar, calc_grammar>
  1019. , proto::_default<calc_grammar>
  1020. >
  1021. , proto::when<
  1022. proto::minus<calc_grammar, calc_grammar>
  1023. , proto::_default<calc_grammar>
  1024. >
  1025. , proto::when<
  1026. proto::multiplies<calc_grammar, calc_grammar>
  1027. , proto::_default<calc_grammar>
  1028. >
  1029. , proto::when<
  1030. proto::divides<calc_grammar, calc_grammar>
  1031. , proto::_default<calc_grammar>
  1032. >
  1033. >
  1034. {};
  1035. With this definition of `calc_grammar` we can evaluate expressions by passing along a Fusion vector containing the values to use for the `_1` and `_2` placeholders:
  1036. int result = calc_grammar()(_1 + _2, fusion::make_vector(3, 4));
  1037. BOOST_ASSERT(result == 7);
  1038. We also want an alternative evaluation strategy that checks for division by zero and throws an exception. Just how ridiculous would it be to copy the entire `calc_grammar` just to change the one line that transforms division expressions?! External transforms are ideally suited to this problem.
  1039. First, we give the division rule in our grammar a "name"; that is, we make it a struct. We'll use this unique type later to dispatch to the right transforms.
  1040. struct calc_grammar;
  1041. struct divides_rule : proto::divides<calc_grammar, calc_grammar> {};
  1042. Next, we change `calc_grammar` to make the handling of division expressions external.
  1043. // The calculator grammar with an external transform for evaluating
  1044. // division expressions.
  1045. struct calc_grammar
  1046. : proto::or_<
  1047. /* ... as before ... */
  1048. , proto::when<
  1049. divides_rule
  1050. , proto::external_transform
  1051. >
  1052. >
  1053. {};
  1054. The use of _external_transform_ above makes the handling of division expressions externally parameterizeable.
  1055. Next, we use _external_transforms_ (note the trailing 's') to capture our evaluation strategy in a bundle that we can pass along to the transform in the data parameter. Read on for the explanation.
  1056. // Evaluate division nodes as before
  1057. struct non_checked_division
  1058. : proto::external_transforms<
  1059. proto::when< divides_rule, proto::_default<calc_grammar> >
  1060. >
  1061. {};
  1062. /* ... */
  1063. non_checked_division non_checked;
  1064. int result2 = calc_grammar()(_1 / _2, fusion::make_vector(6, 2), non_checked);
  1065. The struct `non_cecked_division` associates the transform `proto::_default<calc_grammar>` with the `divides_rule` grammar rule. An instance of that struct is passed along as the third parameter when invoking `calc_grammar`.
  1066. Now, let's implement checked division. The rest should be unsurprising.
  1067. struct division_by_zero : std::exception {};
  1068. struct do_checked_divide : proto::callable
  1069. {
  1070. typedef int result_type;
  1071. int operator()(int left, int right) const
  1072. {
  1073. if (right == 0) throw division_by_zero();
  1074. return left / right;
  1075. }
  1076. };
  1077. struct checked_division
  1078. : proto::external_transforms<
  1079. proto::when<
  1080. divides_rule
  1081. , do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right))
  1082. >
  1083. >
  1084. {};
  1085. /* ... */
  1086. try
  1087. {
  1088. checked_division checked;
  1089. int result3 = calc_grammar_extern()(_1 / _2, fusion::make_vector(6, 0), checked);
  1090. }
  1091. catch(division_by_zero)
  1092. {
  1093. std::cout << "caught division by zero!\n";
  1094. }
  1095. The above code demonstrates how a single grammar can be used with different transforms specified externally. This makes it possible to reuse a grammar to drive several different computations.
  1096. [heading Separating Data From External Transforms]
  1097. As described above, the external transforms feature usurps the data parameter, which is intended to be a place where you can pass arbitrary data, and gives it a specific meaning. But what if you are already using the data parameter for something else? The answer is to use a transform environment. By associating your external transforms with the `proto::transforms` key, you are free to pass arbitrary data in other slots.
  1098. To continue the above example, what if we also needed to pass a piece of data into our transform along with the external transforms? It would look like this:
  1099. int result3 = calc_grammar_extern()(
  1100. _1 / _2
  1101. , fusion::make_vector(6, 0)
  1102. , (proto::data = 42, proto::transforms = checked)
  1103. );
  1104. In the above invocation of the `calc_grammar_extern` algorithm, the map of external transforms is associated with the `proto::transforms` key and passed to the algorithm in a transform environment. Also in the transform environment is a key\/value pair that associates the value `42` with the `proto::data` key.
  1105. [endsect]
  1106. [/====================================================]
  1107. [section:canned_transforms Proto's Built-In Transforms]
  1108. [/====================================================]
  1109. [def _N_ [~N]]
  1110. [def _G_ [~G]]
  1111. [def _G0_ [~G0]]
  1112. [def _G1_ [~G1]]
  1113. [def _CT_ [~CT]]
  1114. [def _OT_ [~OT]]
  1115. [def _ET_ [~ET]]
  1116. [def _ST_ [~ST]]
  1117. [def _FT_ [~FT]]
  1118. Primitive transforms are the building blocks for more interesting composite transforms. Proto defines a bunch of generally useful primitive transforms. They are summarized below.
  1119. [variablelist
  1120. [[_value_pt_]
  1121. [Given a terminal expression, return the value of the terminal.]]
  1122. [[_child_c_pt_]
  1123. [Given a non-terminal expression, `proto::_child_c<_N_>` returns the _N_-th
  1124. child.]]
  1125. [[_child_pt_]
  1126. [A synonym for `proto::_child_c<0>`.]]
  1127. [[_left_pt_]
  1128. [A synonym for `proto::_child_c<0>`.]]
  1129. [[_right_pt_]
  1130. [A synonym for `proto::_child_c<1>`.]]
  1131. [[_expr_pt_]
  1132. [Returns the current expression unmodified.]]
  1133. [[_state_pt_]
  1134. [Returns the current state unmodified.]]
  1135. [[_data_pt_]
  1136. [Returns the current data unmodified.]]
  1137. [[_call_pt_]
  1138. [For a given callable transform `_CT_`, `proto::call<_CT_>` turns the
  1139. callable transform into a primitive transform. This is useful for
  1140. disambiguating callable transforms from object transforms, and also for
  1141. working around compiler bugs with nested function types.]]
  1142. [[_make_pt_]
  1143. [For a given object transform `_OT_`, `proto::make<_OT_>` turns the
  1144. object transform into a primitive transform. This is useful for
  1145. disambiguating object transforms from callable transforms, and also for
  1146. working around compiler bugs with nested function types.]]
  1147. [[_default_pt_]
  1148. [Given a grammar _G_, `proto::_default<_G_>` evaluates the current node
  1149. according to the standard C++ meaning of the operation the node represents.
  1150. For instance, if the current node is a binary plus node, the two children
  1151. will both be evaluated according to `_G_` and the results will be added and
  1152. returned. The return type is deduced with the help of the Boost.Typeof
  1153. library.]]
  1154. [[_fold_pt_]
  1155. [Given three transforms `_ET_`, `_ST_`, and `_FT_`,
  1156. `proto::fold<_ET_, _ST_, _FT_>` first evaluates `_ET_` to obtain a Fusion
  1157. sequence and `_ST_` to obtain an initial state for the fold, and then
  1158. evaluates `_FT_` for each element in the sequence to generate the next
  1159. state from the previous.]]
  1160. [[_reverse_fold_pt_]
  1161. [Like _fold_pt_, except the elements in the Fusion sequence are iterated in
  1162. reverse order.]]
  1163. [[_fold_tree_pt_]
  1164. [Like `proto::fold<_ET_, _ST_, _FT_>`, except that the result of the `_ET_`
  1165. transform is treated as an expression tree that is /flattened/ to generate
  1166. the sequence to be folded. Flattening an expression tree causes child nodes
  1167. with the same tag type as the parent to be put into sequence. For instance,
  1168. `a >> b >> c` would be flattened to the sequence \[`a`, `b`, `c`\], and this
  1169. is the sequence that would be folded.]]
  1170. [[_reverse_fold_tree_pt_]
  1171. [Like _fold_tree_pt_, except that the flattened sequence is iterated in
  1172. reverse order.]]
  1173. [[_lazy_pt_]
  1174. [A combination of _make_pt_ and _call_pt_ that is useful when the nature of
  1175. the transform depends on the expression, state and/or data parameters.
  1176. `proto::lazy<R(A0,A1...An)>` first evaluates `proto::make<R()>` to compute a
  1177. callable type `R2`. Then, it evaluates `proto::call<R2(A0,A1...An)>`.]]
  1178. ]
  1179. [/============================================]
  1180. [heading All Grammars Are Primitive Transforms]
  1181. [/============================================]
  1182. In addition to the above primitive transforms, all of Proto's grammar elements are also primitive transforms. Their behaviors are described below.
  1183. [variablelist
  1184. [[_wild_]
  1185. [Return the current expression unmodified.]]
  1186. [[_or_]
  1187. [For the specified set of alternate sub-grammars, find the one that matches
  1188. the given expression and apply its associated transform.]]
  1189. [[_and_]
  1190. [For the given set of sub-grammars, apply all the associated transforms and
  1191. return the result of the last.]]
  1192. [[_not_]
  1193. [Return the current expression unmodified.]]
  1194. [[_if_]
  1195. [Given three transforms, evaluate the first and treat the result as a
  1196. compile-time Boolean value. If it is true, evaluate the second transform.
  1197. Otherwise, evaluate the third.]]
  1198. [[_switch_]
  1199. [As with _or_, find the sub-grammar that matches the given expression and
  1200. apply its associated transform.]]
  1201. [[_terminal_]
  1202. [Return the current terminal expression unmodified.]]
  1203. [[_plus_, _nary_expr_, et. al.]
  1204. [A Proto grammar that matches a non-terminal such as
  1205. `proto::plus<_G0_, _G1_>`, when used as a primitive transform, creates a new
  1206. plus node where the left child is transformed according to `_G0_` and the
  1207. right child with `_G1_`.]]
  1208. ]
  1209. [/=================================]
  1210. [heading The Pass-Through Transform]
  1211. [/=================================]
  1212. Note the primitive transform associated with grammar elements such as _plus_ described above. They possess a so-called /pass-through/ transform. The pass-through transform accepts an expression of a certain tag type (say, `proto::tag::plus`) and creates a new expression of the same tag type, where each child expression is transformed according to the corresponding child grammar of the pass-through transform. So for instance this grammar ...
  1213. proto::function< X, proto::vararg<Y> >
  1214. ... matches function expressions where the first child matches the `X` grammar and the rest match the `Y` grammar. When used as a transform, the above grammar will create a new function expression where the first child is transformed according to `X` and the rest are transformed according to `Y`.
  1215. The following class templates in Proto can be used as grammars with pass-through transforms:
  1216. [table Class Templates With Pass-Through Transforms
  1217. [[Templates with Pass-Through Transforms]]
  1218. [[`proto::unary_plus<>`]]
  1219. [[`proto::negate<>`]]
  1220. [[`proto::dereference<>`]]
  1221. [[`proto::complement<>`]]
  1222. [[`proto::address_of<>`]]
  1223. [[`proto::logical_not<>`]]
  1224. [[`proto::pre_inc<>`]]
  1225. [[`proto::pre_dec<>`]]
  1226. [[`proto::post_inc<>`]]
  1227. [[`proto::post_dec<>`]]
  1228. [[`proto::shift_left<>`]]
  1229. [[`proto::shift_right<>`]]
  1230. [[`proto::multiplies<>`]]
  1231. [[`proto::divides<>`]]
  1232. [[`proto::modulus<>`]]
  1233. [[`proto::plus<>`]]
  1234. [[`proto::minus<>`]]
  1235. [[`proto::less<>`]]
  1236. [[`proto::greater<>`]]
  1237. [[`proto::less_equal<>`]]
  1238. [[`proto::greater_equal<>`]]
  1239. [[`proto::equal_to<>`]]
  1240. [[`proto::not_equal_to<>`]]
  1241. [[`proto::logical_or<>`]]
  1242. [[`proto::logical_and<>`]]
  1243. [[`proto::bitwise_and<>`]]
  1244. [[`proto::bitwise_or<>`]]
  1245. [[`proto::bitwise_xor<>`]]
  1246. [[`proto::comma<>`]]
  1247. [[`proto::mem_ptr<>`]]
  1248. [[`proto::assign<>`]]
  1249. [[`proto::shift_left_assign<>`]]
  1250. [[`proto::shift_right_assign<>`]]
  1251. [[`proto::multiplies_assign<>`]]
  1252. [[`proto::divides_assign<>`]]
  1253. [[`proto::modulus_assign<>`]]
  1254. [[`proto::plus_assign<>`]]
  1255. [[`proto::minus_assign<>`]]
  1256. [[`proto::bitwise_and_assign<>`]]
  1257. [[`proto::bitwise_or_assign<>`]]
  1258. [[`proto::bitwise_xor_assign<>`]]
  1259. [[`proto::subscript<>`]]
  1260. [[`proto::if_else_<>`]]
  1261. [[`proto::function<>`]]
  1262. [[`proto::unary_expr<>`]]
  1263. [[`proto::binary_expr<>`]]
  1264. [[`proto::nary_expr<>`]]
  1265. ]
  1266. [/=====================================================]
  1267. [heading The Many Roles of Proto Operator Metafunctions]
  1268. [/=====================================================]
  1269. We've seen templates such as _terminal_, _plus_ and _nary_expr_ fill many roles. They are metafunction that generate expression types. They are grammars that match expression types. And they are primitive transforms. The following code samples show examples of each.
  1270. [*As Metafunctions ...]
  1271. // proto::terminal<> and proto::plus<> are metafunctions
  1272. // that generate expression types:
  1273. typedef proto::terminal<int>::type int_;
  1274. typedef proto::plus<int_, int_>::type plus_;
  1275. int_ i = {42}, j = {24};
  1276. plus_ p = {i, j};
  1277. [*As Grammars ...]
  1278. // proto::terminal<> and proto::plus<> are grammars that
  1279. // match expression types
  1280. struct Int : proto::terminal<int> {};
  1281. struct Plus : proto::plus<Int, Int> {};
  1282. BOOST_MPL_ASSERT(( proto::matches< int_, Int > ));
  1283. BOOST_MPL_ASSERT(( proto::matches< plus_, Plus > ));
  1284. [*As Primitive Transforms ...]
  1285. // A transform that removes all unary_plus nodes in an expression
  1286. struct RemoveUnaryPlus
  1287. : proto::or_<
  1288. proto::when<
  1289. proto::unary_plus<RemoveUnaryPlus>
  1290. , RemoveUnaryPlus(proto::_child)
  1291. >
  1292. // Use proto::terminal<> and proto::nary_expr<>
  1293. // both as grammars and as primitive transforms.
  1294. , proto::terminal<_>
  1295. , proto::nary_expr<_, proto::vararg<RemoveUnaryPlus> >
  1296. >
  1297. {};
  1298. int main()
  1299. {
  1300. proto::literal<int> i(0);
  1301. proto::display_expr(
  1302. +i - +(i - +i)
  1303. );
  1304. proto::display_expr(
  1305. RemoveUnaryPlus()( +i - +(i - +i) )
  1306. );
  1307. }
  1308. The above code displays the following, which shows that unary plus nodes have been stripped from the expression:
  1309. [pre
  1310. minus(
  1311. unary_plus(
  1312. terminal(0)
  1313. )
  1314. , unary_plus(
  1315. minus(
  1316. terminal(0)
  1317. , unary_plus(
  1318. terminal(0)
  1319. )
  1320. )
  1321. )
  1322. )
  1323. minus(
  1324. terminal(0)
  1325. , minus(
  1326. terminal(0)
  1327. , terminal(0)
  1328. )
  1329. )
  1330. ]
  1331. [endsect]
  1332. [/======================================================]
  1333. [section:primitives Building Custom Primitive Transforms]
  1334. [/======================================================]
  1335. In previous sections, we've seen how to compose larger transforms out of smaller transforms using function types. The smaller transforms from which larger transforms are composed are /primitive transforms/, and Proto provides a bunch of common ones such as `_child0` and `_value`. In this section we'll see how to author your own primitive transforms.
  1336. [note There are a few reasons why you might want to write your own primitive transforms. For instance, your transform may be complicated, and composing it out of primitives becomes unwieldy. You might also need to work around compiler bugs on legacy compilers that make composing transforms using function types problematic. Finally, you might also decide to define your own primitive transforms to improve compile times. Since Proto can simply invoke a primitive transform directly without having to process arguments or differentiate callable transforms from object transforms, primitive transforms are more efficient.]
  1337. Primitive transforms inherit from `proto::transform<>` and have a nested `impl<>` template that inherits from `proto::transform_impl<>`. For example, this is how Proto defines the `_child_c<_N_>` transform, which returns the _N_-th child of the current expression:
  1338. namespace boost { namespace proto
  1339. {
  1340. // A primitive transform that returns N-th child
  1341. // of the current expression.
  1342. template<int N>
  1343. struct _child_c : transform<_child_c<N> >
  1344. {
  1345. template<typename Expr, typename State, typename Data>
  1346. struct impl : transform_impl<Expr, State, Data>
  1347. {
  1348. typedef
  1349. typename result_of::child_c<Expr, N>::type
  1350. result_type;
  1351. result_type operator ()(
  1352. typename impl::expr_param expr
  1353. , typename impl::state_param state
  1354. , typename impl::data_param data
  1355. ) const
  1356. {
  1357. return proto::child_c<N>(expr);
  1358. }
  1359. };
  1360. };
  1361. // Note that _child_c<N> is callable, so that
  1362. // it can be used in callable transforms, as:
  1363. // _child_c<0>(_child_c<1>)
  1364. template<int N>
  1365. struct is_callable<_child_c<N> >
  1366. : mpl::true_
  1367. {};
  1368. }}
  1369. The `proto::transform<>` base class provides the `operator()` overloads and the nested `result<>` template that make your transform a valid function object. These are implemented in terms of the nested `impl<>` template you define.
  1370. The `proto::transform_impl<>` base class is a convenience. It provides some nested typedefs that are generally useful. They are specified in the table below:
  1371. [table proto::transform_impl<Expr, State, Data> typedefs
  1372. [[typedef][Equivalent To]]
  1373. [[`expr`][`typename remove_reference<Expr>::type`]]
  1374. [[`state`][`typename remove_reference<State>::type`]]
  1375. [[`data`][`typename remove_reference<Data>::type`]]
  1376. [[`expr_param`][`typename add_reference<typename add_const<Expr>::type>::type`]]
  1377. [[`state_param`][`typename add_reference<typename add_const<State>::type>::type`]]
  1378. [[`data_param`][`typename add_reference<typename add_const<Data>::type>::type`]]
  1379. ]
  1380. You'll notice that `_child_c::impl::operator()` takes arguments of types `expr_param`, `state_param`, and `data_param`. The typedefs make it easy to accept arguments by reference or const reference accordingly.
  1381. The only other interesting bit is the `is_callable<>` specialization, which will be described in the [link boost_proto.users_guide.back_end.expression_transformation.is_callable next section].
  1382. [endsect]
  1383. [/=================================================]
  1384. [section:is_callable Making Your Transform Callable]
  1385. [/=================================================]
  1386. Transforms are typically of the form `proto::when< Something, R(A0,A1,...) >`. The question is whether `R` represents a function to call or an object to construct, and the answer determines how _when_ evaluates the transform. _when_ uses the `proto::is_callable<>` trait to disambiguate between the two. Proto does its best to guess whether a type is callable or not, but it doesn't always get it right. It's best to know the rules Proto uses, so that you know when you need to be more explicit.
  1387. For most types `R`, `proto::is_callable<R>` checks for inheritance from `proto::callable`. However, if the type `R` is a template specialization, Proto assumes that it is /not/ callable ['even if the template inherits from `proto::callable`]. We'll see why in a minute. Consider the following erroneous callable object:
  1388. // Proto can't tell this defines something callable!
  1389. template<typename T>
  1390. struct times2 : proto::callable
  1391. {
  1392. typedef T result_type;
  1393. T operator()(T i) const
  1394. {
  1395. return i * 2;
  1396. }
  1397. };
  1398. // ERROR! This is not going to multiply the int by 2:
  1399. struct IntTimes2
  1400. : proto::when<
  1401. proto::terminal<int>
  1402. , times2<int>(proto::_value)
  1403. >
  1404. {};
  1405. The problem is that Proto doesn't know that `times2<int>` is callable, so rather that invoking the `times2<int>` function object, Proto will try to construct a `times2<int>` object and initialize it will an `int`. That will not compile.
  1406. [note Why can't Proto tell that `times2<int>` is callable? After all, it inherits from `proto::callable`, and that is detectable, right? The problem is that merely asking whether some type `X<Y>` inherits from `callable` will cause the template `X<Y>` to be instantiated. That's a problem for a type like `std::vector<_value(_child1)>`. `std::vector<>` will not suffer to be instantiated with `_value(_child1)` as a template parameter. Since merely asking the question will sometimes result in a hard error, Proto can't ask; it has to assume that `X<Y>` represents an object to construct and not a function to call.]
  1407. There are a couple of solutions to the `times2<int>` problem. One solution is to wrap the transform in `proto::call<>`. This forces Proto to treat `times2<int>` as callable:
  1408. // OK, calls times2<int>
  1409. struct IntTimes2
  1410. : proto::when<
  1411. proto::terminal<int>
  1412. , proto::call<times2<int>(proto::_value)>
  1413. >
  1414. {};
  1415. This can be a bit of a pain, because we need to wrap every use of `times2<int>`, which can be tedious and error prone, and makes our grammar cluttered and harder to read.
  1416. Another solution is to specialize `proto::is_callable<>` on our `times2<>` template:
  1417. namespace boost { namespace proto
  1418. {
  1419. // Tell Proto that times2<> is callable
  1420. template<typename T>
  1421. struct is_callable<times2<T> >
  1422. : mpl::true_
  1423. {};
  1424. }}
  1425. // OK, times2<> is callable
  1426. struct IntTimes2
  1427. : proto::when<
  1428. proto::terminal<int>
  1429. , times2<int>(proto::_value)
  1430. >
  1431. {};
  1432. This is better, but still a pain because of the need to open Proto's namespace.
  1433. You could simply make sure that the callable type is not a template specialization. Consider the following:
  1434. // No longer a template specialization!
  1435. struct times2int : times2<int> {};
  1436. // OK, times2int is callable
  1437. struct IntTimes2
  1438. : proto::when<
  1439. proto::terminal<int>
  1440. , times2int(proto::_value)
  1441. >
  1442. {};
  1443. This works because now Proto can tell that `times2int` inherits (indirectly) from `proto::callable`. Any non-template types can be safely checked for inheritance because, as they are not templates, there is no worry about instantiation errors.
  1444. There is one last way to tell Proto that `times2<>` is callable. You could add an extra dummy template parameter that defaults to `proto::callable`:
  1445. // Proto will recognize this as callable
  1446. template<typename T, typename Callable = proto::callable>
  1447. struct times2 : proto::callable
  1448. {
  1449. typedef T result_type;
  1450. T operator()(T i) const
  1451. {
  1452. return i * 2;
  1453. }
  1454. };
  1455. // OK, this works!
  1456. struct IntTimes2
  1457. : proto::when<
  1458. proto::terminal<int>
  1459. , times2<int>(proto::_value)
  1460. >
  1461. {};
  1462. Note that in addition to the extra template parameter, `times2<>` still inherits from `proto::callable`. That's not necessary in this example but it is good style because any types derived from `times2<>` (as `times2int` defined above) will still be considered callable.
  1463. [endsect]
  1464. [endsect]
  1465. [endsect]