annotate src/capnproto-git-20161025/c++/samples/calculator-client.c++ @ 133:1ac99bfc383d

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