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