annotate src/capnproto-git-20161025/c++/samples/calculator-client.c++ @ 83:ae30d91d2ffe

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