1[/ 2 / Copyright (c) 2008 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[/=======================] 9[section Hello Calculator] 10[/=======================] 11 12"Hello, world" is nice, but it doesn't get you very far. Let's use Proto to build a 13EDSL (embedded domain-specific language) for a lazily-evaluated calculator. We'll 14see how to define the terminals in your mini-language, how to compose them into 15larger expressions, and how to define an evaluation context so that your 16expressions can do useful work. When we're done, we'll have a mini-language that 17will allow us to declare a lazily-evaluated arithmetic expression, such as 18`(_2 - _1) / _2 * 100`, where `_1` and `_2` are placeholders for values to be 19passed in when the expression is evaluated. 20 21[/=========================] 22[heading Defining Terminals] 23[/=========================] 24 25The first order of business is to define the placeholders `_1` and `_2`. For that, 26we'll use the _terminal_ metafunction. 27 28 // Define a placeholder type 29 template<int I> 30 struct placeholder 31 {}; 32 33 // Define the Protofied placeholder terminals 34 proto::terminal<placeholder<0> >::type const _1 = {{}}; 35 proto::terminal<placeholder<1> >::type const _2 = {{}}; 36 37The initialization may look a little odd at first, but there is a good reason 38for doing things this way. The objects `_1` and `_2` above do not require 39run-time construction -- they are ['statically initialized], which means they 40are essentially initialized at compile time. See the 41[link boost_proto.appendices.rationale.static_initialization Static 42Initialization] section in the [link boost_proto.appendices.rationale Rationale] 43appendix for more information. 44 45[/====================================] 46[heading Constructing Expression Trees] 47[/====================================] 48 49Now that we have terminals, we can use Proto's operator overloads to combine 50these terminals into larger expressions. So, for instance, we can immediately 51say things like: 52 53 // This builds an expression template 54 (_2 - _1) / _2 * 100; 55 56This creates an expression tree with a node for each operator. The type of the 57resulting object is large and complex, but we are not terribly interested in it right now. 58 59So far, the object is just a tree representing the expression. It has no 60behavior. In particular, it is not yet a calculator. Below we'll see how 61to make it a calculator by defining an evaluation context. 62 63[/==================================] 64[heading Evaluating Expression Trees] 65[/==================================] 66 67No doubt you want your expression templates to actually /do/ something. One 68approach is to define an ['evaluation context]. The context is like a function 69object that associates behaviors with the node types in your expression tree. 70The following example should make it clear. It is explained below. 71 72 struct calculator_context 73 : proto::callable_context< calculator_context const > 74 { 75 // Values to replace the placeholders 76 std::vector<double> args; 77 78 // Define the result type of the calculator. 79 // (This makes the calculator_context "callable".) 80 typedef double result_type; 81 82 // Handle the placeholders: 83 template<int I> 84 double operator()(proto::tag::terminal, placeholder<I>) const 85 { 86 return this->args[I]; 87 } 88 }; 89 90In `calculator_context`, we specify how Proto should evaluate the placeholder 91terminals by defining the appropriate overloads of the function call operator. 92For any other nodes in the expression tree (e.g., arithmetic operations or 93non-placeholder terminals), Proto will evaluate the expression in the "default" 94way. For example, a binary plus node is evaluated by first evaluating the left 95and right operands and adding the results. Proto's default evaluator uses the 96_typeof_ library to compute return types. 97 98Now that we have an evaluation context for our calculator, we can use it to 99evaluate our arithmetic expressions, as below: 100 101 calculator_context ctx; 102 ctx.args.push_back(45); // the value of _1 is 45 103 ctx.args.push_back(50); // the value of _2 is 50 104 105 // Create an arithmetic expression and immediately evaluate it 106 double d = proto::eval( (_2 - _1) / _2 * 100, ctx ); 107 108 // This prints "10" 109 std::cout << d << std::endl; 110 111Later, we'll see how to define more interesting evaluation contexts and 112expression transforms that give you total control over how your expressions 113are evaluated. 114 115[/===================================] 116[heading Customizing Expression Trees] 117[/===================================] 118 119Our calculator EDSL is already pretty useful, and for many EDSL scenarios, no more 120would be needed. But let's keep going. Imagine how much nicer it would be if all 121calculator expressions overloaded `operator()` so that they could be used as 122function objects. We can do that by creating a calculator /domain/ and telling 123Proto that all expressions in the calculator domain have extra members. Here is how 124to define a calculator domain: 125 126 // Forward-declare an expression wrapper 127 template<typename Expr> 128 struct calculator; 129 130 // Define a calculator domain. Expression within 131 // the calculator domain will be wrapped in the 132 // calculator<> expression wrapper. 133 struct calculator_domain 134 : proto::domain< proto::generator<calculator> > 135 {}; 136 137The `calculator<>` type will be an expression wrapper. It will behave just like the 138expression that it wraps, but it will have extra member functions that we will 139define. The `calculator_domain` is what informs Proto about our wrapper. It is used 140below in the definition of `calculator<>`. Read on for a description. 141 142 // Define a calculator expression wrapper. It behaves just like 143 // the expression it wraps, but with an extra operator() member 144 // function that evaluates the expression. 145 template<typename Expr> 146 struct calculator 147 : proto::extends<Expr, calculator<Expr>, calculator_domain> 148 { 149 typedef 150 proto::extends<Expr, calculator<Expr>, calculator_domain> 151 base_type; 152 153 calculator(Expr const &expr = Expr()) 154 : base_type(expr) 155 {} 156 157 typedef double result_type; 158 159 // Overload operator() to invoke proto::eval() with 160 // our calculator_context. 161 double operator()(double a1 = 0, double a2 = 0) const 162 { 163 calculator_context ctx; 164 ctx.args.push_back(a1); 165 ctx.args.push_back(a2); 166 167 return proto::eval(*this, ctx); 168 } 169 }; 170 171The `calculator<>` struct is an expression /extension/. It uses `proto::extends<>` to effectively add additional members to an expression type. When composing larger expressions from smaller ones, Proto notes what domain the smaller expressions are in. The larger expression is in the same domain and is automatically wrapped in the domain's extension wrapper. 172 173All that remains to be done is to put our placeholders in the calculator domain. We do that by wrapping them in our `calculator<>` wrapper, as below: 174 175 // Define the Protofied placeholder terminals, in the 176 // calculator domain. 177 calculator<proto::terminal<placeholder<0> >::type> const _1; 178 calculator<proto::terminal<placeholder<1> >::type> const _2; 179 180Any larger expression that contain these placeholders will automatically be wrapped in the `calculator<>` wrapper and have our `operator()` overload. That means we can use them as function objects as follows. 181 182 double result = ((_2 - _1) / _2 * 100)(45.0, 50.0); 183 assert(result == (50.0 - 45.0) / 50.0 * 100)); 184 185Since calculator expressions are now valid function objects, we can use them with standard algorithms, as shown below: 186 187 double a1[4] = { 56, 84, 37, 69 }; 188 double a2[4] = { 65, 120, 60, 70 }; 189 double a3[4] = { 0 }; 190 191 // Use std::transform() and a calculator expression 192 // to calculate percentages given two input sequences: 193 std::transform(a1, a1+4, a2, a3, (_2 - _1) / _2 * 100); 194 195Now, let's use the calculator example to explore some other useful features of Proto. 196 197[/====================================] 198[heading Detecting Invalid Expressions] 199[/====================================] 200 201You may have noticed that you didn't have to define an overloaded `operator-()` or 202`operator/()` -- Proto defined them for you. In fact, Proto overloads /all/ the 203operators for you, even though they may not mean anything in your domain-specific 204language. That means it may be possible to create expressions that are invalid in 205your domain. You can detect invalid expressions with Proto by defining the 206/grammar/ of your domain-specific language. 207 208For simplicity, assume that our calculator EDSL should only allow addition, 209subtraction, multiplication and division. Any expression involving any other 210operator is invalid. Using Proto, we can state this requirement by defining the 211grammar of the calculator EDSL. It looks as follows: 212 213 // Define the grammar of calculator expressions 214 struct calculator_grammar 215 : proto::or_< 216 proto::plus< calculator_grammar, calculator_grammar > 217 , proto::minus< calculator_grammar, calculator_grammar > 218 , proto::multiplies< calculator_grammar, calculator_grammar > 219 , proto::divides< calculator_grammar, calculator_grammar > 220 , proto::terminal< proto::_ > 221 > 222 {}; 223 224You can read the above grammar as follows: an expression tree conforms to the calculator grammar if it is a binary plus, minus, multiplies or divides node, where both child nodes also conform to the calculator grammar; or if it is a terminal. In a Proto grammar, _wild_ is a wildcard that matches any type, so `proto::terminal< proto::_ >` matches any terminal, whether it is a placeholder or a literal. 225 226[note This grammar is actually a little looser than we would like. Only placeholders and literals that are convertible to doubles are valid terminals. Later on we'll see how to express things like that in Proto grammars.] 227 228Once you have defined the grammar of your EDSL, you can use the _matches_ metafunction to check whether a given expression type conforms to the grammar. For instance, we might add the following to our `calculator::operator()` overload: 229 230 template<typename Expr> 231 struct calculator 232 : proto::extends< /* ... as before ... */ > 233 { 234 /* ... */ 235 double operator()(double a1 = 0, double a2 = 0) const 236 { 237 // Check here that the expression we are about to 238 // evaluate actually conforms to the calculator grammar. 239 BOOST_MPL_ASSERT((proto::matches<Expr, calculator_grammar>)); 240 /* ... */ 241 } 242 }; 243 244The addition of the `BOOST_MPL_ASSERT()` line enforces at compile time that we only 245evaluate expressions that conform to the calculator EDSL's grammar. With Proto 246grammars, `proto::matches<>` and `BOOST_MPL_ASSERT()` it is very easy to give the 247users of your EDSL short and readable compile-time errors when they accidentally 248misuse your EDSL. 249 250[note `BOOST_MPL_ASSERT()` is part of the Boost Metaprogramming Library. To use it, 251just `#include <boost/mpl/assert.hpp>`.] 252 253[/=====================================] 254[heading Controlling Operator Overloads] 255[/=====================================] 256 257Grammars and `proto::matches<>` make it possible to detect when a user has created 258an invalid expression and issue a compile-time error. But what if you want to 259prevent users from creating invalid expressions in the first place? By using 260grammars and domains together, you can disable any of Proto's operator overloads 261that would create an invalid expression. It is as simple as specifying the EDSL's 262grammar when you define the domain, as shown below: 263 264 // Define a calculator domain. Expression within 265 // the calculator domain will be wrapped in the 266 // calculator<> expression wrapper. 267 // NEW: Any operator overloads that would create an 268 // expression that does not conform to the 269 // calculator grammar is automatically disabled. 270 struct calculator_domain 271 : proto::domain< proto::generator<calculator>, calculator_grammar > 272 {}; 273 274The only thing we changed is we added `calculator_grammar` as the second template 275parameter to the `proto::domain<>` template when defining `calculator_domain`. With 276this simple addition, we disable any of Proto's operator overloads that would 277create an invalid calculator expression. 278 279[/========================] 280[heading ... And Much More] 281[/========================] 282 283Hopefully, this gives you an idea of what sorts of things Proto can do for you. But 284this only scratches the surface. The rest of this users' guide will describe all 285these features and others in more detail. 286 287Happy metaprogramming! 288 289[endsect] 290