Mercurial > hg > sv-dependency-builds
comparison src/capnproto-0.6.0/c++/samples/calculator-client.c++ @ 147:45360b968bf4
Cap'n Proto v0.6 + build for OSX
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Mon, 22 May 2017 10:01:37 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
146:206f0eb279b8 | 147:45360b968bf4 |
---|---|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors | |
2 // Licensed under the MIT License: | |
3 // | |
4 // Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 // of this software and associated documentation files (the "Software"), to deal | |
6 // in the Software without restriction, including without limitation the rights | |
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 // copies of the Software, and to permit persons to whom the Software is | |
9 // furnished to do so, subject to the following conditions: | |
10 // | |
11 // The above copyright notice and this permission notice shall be included in | |
12 // all copies or substantial portions of the Software. | |
13 // | |
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 // THE SOFTWARE. | |
21 | |
22 #include "calculator.capnp.h" | |
23 #include <capnp/ez-rpc.h> | |
24 #include <kj/debug.h> | |
25 #include <math.h> | |
26 #include <iostream> | |
27 | |
28 class PowerFunction final: public Calculator::Function::Server { | |
29 // An implementation of the Function interface wrapping pow(). Note that | |
30 // we're implementing this on the client side and will pass a reference to | |
31 // the server. The server will then be able to make calls back to the client. | |
32 | |
33 public: | |
34 kj::Promise<void> call(CallContext context) { | |
35 auto params = context.getParams().getParams(); | |
36 KJ_REQUIRE(params.size() == 2, "Wrong number of parameters."); | |
37 context.getResults().setValue(pow(params[0], params[1])); | |
38 return kj::READY_NOW; | |
39 } | |
40 }; | |
41 | |
42 int main(int argc, const char* argv[]) { | |
43 if (argc != 2) { | |
44 std::cerr << "usage: " << argv[0] << " HOST:PORT\n" | |
45 "Connects to the Calculator server at the given address and " | |
46 "does some RPCs." << std::endl; | |
47 return 1; | |
48 } | |
49 | |
50 capnp::EzRpcClient client(argv[1]); | |
51 Calculator::Client calculator = client.getMain<Calculator>(); | |
52 | |
53 // Keep an eye on `waitScope`. Whenever you see it used is a place where we | |
54 // stop and wait for the server to respond. If a line of code does not use | |
55 // `waitScope`, then it does not block! | |
56 auto& waitScope = client.getWaitScope(); | |
57 | |
58 { | |
59 // Make a request that just evaluates the literal value 123. | |
60 // | |
61 // What's interesting here is that evaluate() returns a "Value", which is | |
62 // another interface and therefore points back to an object living on the | |
63 // server. We then have to call read() on that object to read it. | |
64 // However, even though we are making two RPC's, this block executes in | |
65 // *one* network round trip because of promise pipelining: we do not wait | |
66 // for the first call to complete before we send the second call to the | |
67 // server. | |
68 | |
69 std::cout << "Evaluating a literal... "; | |
70 std::cout.flush(); | |
71 | |
72 // Set up the request. | |
73 auto request = calculator.evaluateRequest(); | |
74 request.getExpression().setLiteral(123); | |
75 | |
76 // Send it, which returns a promise for the result (without blocking). | |
77 auto evalPromise = request.send(); | |
78 | |
79 // Using the promise, create a pipelined request to call read() on the | |
80 // returned object, and then send that. | |
81 auto readPromise = evalPromise.getValue().readRequest().send(); | |
82 | |
83 // Now that we've sent all the requests, wait for the response. Until this | |
84 // point, we haven't waited at all! | |
85 auto response = readPromise.wait(waitScope); | |
86 KJ_ASSERT(response.getValue() == 123); | |
87 | |
88 std::cout << "PASS" << std::endl; | |
89 } | |
90 | |
91 { | |
92 // Make a request to evaluate 123 + 45 - 67. | |
93 // | |
94 // The Calculator interface requires that we first call getOperator() to | |
95 // get the addition and subtraction functions, then call evaluate() to use | |
96 // them. But, once again, we can get both functions, call evaluate(), and | |
97 // then read() the result -- four RPCs -- in the time of *one* network | |
98 // round trip, because of promise pipelining. | |
99 | |
100 std::cout << "Using add and subtract... "; | |
101 std::cout.flush(); | |
102 | |
103 Calculator::Function::Client add = nullptr; | |
104 Calculator::Function::Client subtract = nullptr; | |
105 | |
106 { | |
107 // Get the "add" function from the server. | |
108 auto request = calculator.getOperatorRequest(); | |
109 request.setOp(Calculator::Operator::ADD); | |
110 add = request.send().getFunc(); | |
111 } | |
112 | |
113 { | |
114 // Get the "subtract" function from the server. | |
115 auto request = calculator.getOperatorRequest(); | |
116 request.setOp(Calculator::Operator::SUBTRACT); | |
117 subtract = request.send().getFunc(); | |
118 } | |
119 | |
120 // Build the request to evaluate 123 + 45 - 67. | |
121 auto request = calculator.evaluateRequest(); | |
122 | |
123 auto subtractCall = request.getExpression().initCall(); | |
124 subtractCall.setFunction(subtract); | |
125 auto subtractParams = subtractCall.initParams(2); | |
126 subtractParams[1].setLiteral(67); | |
127 | |
128 auto addCall = subtractParams[0].initCall(); | |
129 addCall.setFunction(add); | |
130 auto addParams = addCall.initParams(2); | |
131 addParams[0].setLiteral(123); | |
132 addParams[1].setLiteral(45); | |
133 | |
134 // Send the evaluate() request, read() the result, and wait for read() to | |
135 // finish. | |
136 auto evalPromise = request.send(); | |
137 auto readPromise = evalPromise.getValue().readRequest().send(); | |
138 | |
139 auto response = readPromise.wait(waitScope); | |
140 KJ_ASSERT(response.getValue() == 101); | |
141 | |
142 std::cout << "PASS" << std::endl; | |
143 } | |
144 | |
145 { | |
146 // Make a request to evaluate 4 * 6, then use the result in two more | |
147 // requests that add 3 and 5. | |
148 // | |
149 // Since evaluate() returns its result wrapped in a `Value`, we can pass | |
150 // that `Value` back to the server in subsequent requests before the first | |
151 // `evaluate()` has actually returned. Thus, this example again does only | |
152 // one network round trip. | |
153 | |
154 std::cout << "Pipelining eval() calls... "; | |
155 std::cout.flush(); | |
156 | |
157 Calculator::Function::Client add = nullptr; | |
158 Calculator::Function::Client multiply = nullptr; | |
159 | |
160 { | |
161 // Get the "add" function from the server. | |
162 auto request = calculator.getOperatorRequest(); | |
163 request.setOp(Calculator::Operator::ADD); | |
164 add = request.send().getFunc(); | |
165 } | |
166 | |
167 { | |
168 // Get the "multiply" function from the server. | |
169 auto request = calculator.getOperatorRequest(); | |
170 request.setOp(Calculator::Operator::MULTIPLY); | |
171 multiply = request.send().getFunc(); | |
172 } | |
173 | |
174 // Build the request to evaluate 4 * 6 | |
175 auto request = calculator.evaluateRequest(); | |
176 | |
177 auto multiplyCall = request.getExpression().initCall(); | |
178 multiplyCall.setFunction(multiply); | |
179 auto multiplyParams = multiplyCall.initParams(2); | |
180 multiplyParams[0].setLiteral(4); | |
181 multiplyParams[1].setLiteral(6); | |
182 | |
183 auto multiplyResult = request.send().getValue(); | |
184 | |
185 // Use the result in two calls that add 3 and add 5. | |
186 | |
187 auto add3Request = calculator.evaluateRequest(); | |
188 auto add3Call = add3Request.getExpression().initCall(); | |
189 add3Call.setFunction(add); | |
190 auto add3Params = add3Call.initParams(2); | |
191 add3Params[0].setPreviousResult(multiplyResult); | |
192 add3Params[1].setLiteral(3); | |
193 auto add3Promise = add3Request.send().getValue().readRequest().send(); | |
194 | |
195 auto add5Request = calculator.evaluateRequest(); | |
196 auto add5Call = add5Request.getExpression().initCall(); | |
197 add5Call.setFunction(add); | |
198 auto add5Params = add5Call.initParams(2); | |
199 add5Params[0].setPreviousResult(multiplyResult); | |
200 add5Params[1].setLiteral(5); | |
201 auto add5Promise = add5Request.send().getValue().readRequest().send(); | |
202 | |
203 // Now wait for the results. | |
204 KJ_ASSERT(add3Promise.wait(waitScope).getValue() == 27); | |
205 KJ_ASSERT(add5Promise.wait(waitScope).getValue() == 29); | |
206 | |
207 std::cout << "PASS" << std::endl; | |
208 } | |
209 | |
210 { | |
211 // Our calculator interface supports defining functions. Here we use it | |
212 // to define two functions and then make calls to them as follows: | |
213 // | |
214 // f(x, y) = x * 100 + y | |
215 // g(x) = f(x, x + 1) * 2; | |
216 // f(12, 34) | |
217 // g(21) | |
218 // | |
219 // Once again, the whole thing takes only one network round trip. | |
220 | |
221 std::cout << "Defining functions... "; | |
222 std::cout.flush(); | |
223 | |
224 Calculator::Function::Client add = nullptr; | |
225 Calculator::Function::Client multiply = nullptr; | |
226 Calculator::Function::Client f = nullptr; | |
227 Calculator::Function::Client g = nullptr; | |
228 | |
229 { | |
230 // Get the "add" function from the server. | |
231 auto request = calculator.getOperatorRequest(); | |
232 request.setOp(Calculator::Operator::ADD); | |
233 add = request.send().getFunc(); | |
234 } | |
235 | |
236 { | |
237 // Get the "multiply" function from the server. | |
238 auto request = calculator.getOperatorRequest(); | |
239 request.setOp(Calculator::Operator::MULTIPLY); | |
240 multiply = request.send().getFunc(); | |
241 } | |
242 | |
243 { | |
244 // Define f. | |
245 auto request = calculator.defFunctionRequest(); | |
246 request.setParamCount(2); | |
247 | |
248 { | |
249 // Build the function body. | |
250 auto addCall = request.getBody().initCall(); | |
251 addCall.setFunction(add); | |
252 auto addParams = addCall.initParams(2); | |
253 addParams[1].setParameter(1); // y | |
254 | |
255 auto multiplyCall = addParams[0].initCall(); | |
256 multiplyCall.setFunction(multiply); | |
257 auto multiplyParams = multiplyCall.initParams(2); | |
258 multiplyParams[0].setParameter(0); // x | |
259 multiplyParams[1].setLiteral(100); | |
260 } | |
261 | |
262 f = request.send().getFunc(); | |
263 } | |
264 | |
265 { | |
266 // Define g. | |
267 auto request = calculator.defFunctionRequest(); | |
268 request.setParamCount(1); | |
269 | |
270 { | |
271 // Build the function body. | |
272 auto multiplyCall = request.getBody().initCall(); | |
273 multiplyCall.setFunction(multiply); | |
274 auto multiplyParams = multiplyCall.initParams(2); | |
275 multiplyParams[1].setLiteral(2); | |
276 | |
277 auto fCall = multiplyParams[0].initCall(); | |
278 fCall.setFunction(f); | |
279 auto fParams = fCall.initParams(2); | |
280 fParams[0].setParameter(0); | |
281 | |
282 auto addCall = fParams[1].initCall(); | |
283 addCall.setFunction(add); | |
284 auto addParams = addCall.initParams(2); | |
285 addParams[0].setParameter(0); | |
286 addParams[1].setLiteral(1); | |
287 } | |
288 | |
289 g = request.send().getFunc(); | |
290 } | |
291 | |
292 // OK, we've defined all our functions. Now create our eval requests. | |
293 | |
294 // f(12, 34) | |
295 auto fEvalRequest = calculator.evaluateRequest(); | |
296 auto fCall = fEvalRequest.initExpression().initCall(); | |
297 fCall.setFunction(f); | |
298 auto fParams = fCall.initParams(2); | |
299 fParams[0].setLiteral(12); | |
300 fParams[1].setLiteral(34); | |
301 auto fEvalPromise = fEvalRequest.send().getValue().readRequest().send(); | |
302 | |
303 // g(21) | |
304 auto gEvalRequest = calculator.evaluateRequest(); | |
305 auto gCall = gEvalRequest.initExpression().initCall(); | |
306 gCall.setFunction(g); | |
307 gCall.initParams(1)[0].setLiteral(21); | |
308 auto gEvalPromise = gEvalRequest.send().getValue().readRequest().send(); | |
309 | |
310 // Wait for the results. | |
311 KJ_ASSERT(fEvalPromise.wait(waitScope).getValue() == 1234); | |
312 KJ_ASSERT(gEvalPromise.wait(waitScope).getValue() == 4244); | |
313 | |
314 std::cout << "PASS" << std::endl; | |
315 } | |
316 | |
317 { | |
318 // Make a request that will call back to a function defined locally. | |
319 // | |
320 // Specifically, we will compute 2^(4 + 5). However, exponent is not | |
321 // defined by the Calculator server. So, we'll implement the Function | |
322 // interface locally and pass it to the server for it to use when | |
323 // evaluating the expression. | |
324 // | |
325 // This example requires two network round trips to complete, because the | |
326 // server calls back to the client once before finishing. In this | |
327 // particular case, this could potentially be optimized by using a tail | |
328 // call on the server side -- see CallContext::tailCall(). However, to | |
329 // keep the example simpler, we haven't implemented this optimization in | |
330 // the sample server. | |
331 | |
332 std::cout << "Using a callback... "; | |
333 std::cout.flush(); | |
334 | |
335 Calculator::Function::Client add = nullptr; | |
336 | |
337 { | |
338 // Get the "add" function from the server. | |
339 auto request = calculator.getOperatorRequest(); | |
340 request.setOp(Calculator::Operator::ADD); | |
341 add = request.send().getFunc(); | |
342 } | |
343 | |
344 // Build the eval request for 2^(4+5). | |
345 auto request = calculator.evaluateRequest(); | |
346 | |
347 auto powCall = request.getExpression().initCall(); | |
348 powCall.setFunction(kj::heap<PowerFunction>()); | |
349 auto powParams = powCall.initParams(2); | |
350 powParams[0].setLiteral(2); | |
351 | |
352 auto addCall = powParams[1].initCall(); | |
353 addCall.setFunction(add); | |
354 auto addParams = addCall.initParams(2); | |
355 addParams[0].setLiteral(4); | |
356 addParams[1].setLiteral(5); | |
357 | |
358 // Send the request and wait. | |
359 auto response = request.send().getValue().readRequest() | |
360 .send().wait(waitScope); | |
361 KJ_ASSERT(response.getValue() == 512); | |
362 | |
363 std::cout << "PASS" << std::endl; | |
364 } | |
365 | |
366 return 0; | |
367 } |