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