comparison src/capnproto-0.6.0/c++/samples/calculator-client.c++ @ 62:0994c39f1e94

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
61:d101c4099725 62:0994c39f1e94
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 }