annotate src/capnproto-git-20161025/c++/samples/calculator-server.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 <kj/debug.h>
cannam@48 24 #include <capnp/ez-rpc.h>
cannam@48 25 #include <capnp/message.h>
cannam@48 26 #include <iostream>
cannam@48 27
cannam@48 28 typedef unsigned int uint;
cannam@48 29
cannam@48 30 kj::Promise<double> readValue(Calculator::Value::Client value) {
cannam@48 31 // Helper function to asynchronously call read() on a Calculator::Value and
cannam@48 32 // return a promise for the result. (In the future, the generated code might
cannam@48 33 // include something like this automatically.)
cannam@48 34
cannam@48 35 return value.readRequest().send()
cannam@48 36 .then([](capnp::Response<Calculator::Value::ReadResults> result) {
cannam@48 37 return result.getValue();
cannam@48 38 });
cannam@48 39 }
cannam@48 40
cannam@48 41 kj::Promise<double> evaluateImpl(
cannam@48 42 Calculator::Expression::Reader expression,
cannam@48 43 capnp::List<double>::Reader params = capnp::List<double>::Reader()) {
cannam@48 44 // Implementation of CalculatorImpl::evaluate(), also shared by
cannam@48 45 // FunctionImpl::call(). In the latter case, `params` are the parameter
cannam@48 46 // values passed to the function; in the former case, `params` is just an
cannam@48 47 // empty list.
cannam@48 48
cannam@48 49 switch (expression.which()) {
cannam@48 50 case Calculator::Expression::LITERAL:
cannam@48 51 return expression.getLiteral();
cannam@48 52
cannam@48 53 case Calculator::Expression::PREVIOUS_RESULT:
cannam@48 54 return readValue(expression.getPreviousResult());
cannam@48 55
cannam@48 56 case Calculator::Expression::PARAMETER: {
cannam@48 57 KJ_REQUIRE(expression.getParameter() < params.size(),
cannam@48 58 "Parameter index out-of-range.");
cannam@48 59 return params[expression.getParameter()];
cannam@48 60 }
cannam@48 61
cannam@48 62 case Calculator::Expression::CALL: {
cannam@48 63 auto call = expression.getCall();
cannam@48 64 auto func = call.getFunction();
cannam@48 65
cannam@48 66 // Evaluate each parameter.
cannam@48 67 kj::Array<kj::Promise<double>> paramPromises =
cannam@48 68 KJ_MAP(param, call.getParams()) {
cannam@48 69 return evaluateImpl(param, params);
cannam@48 70 };
cannam@48 71
cannam@48 72 // Join the array of promises into a promise for an array.
cannam@48 73 kj::Promise<kj::Array<double>> joinedParams =
cannam@48 74 kj::joinPromises(kj::mv(paramPromises));
cannam@48 75
cannam@48 76 // When the parameters are complete, call the function.
cannam@48 77 return joinedParams.then([func](kj::Array<double>&& paramValues) mutable {
cannam@48 78 auto request = func.callRequest();
cannam@48 79 request.setParams(paramValues);
cannam@48 80 return request.send().then(
cannam@48 81 [](capnp::Response<Calculator::Function::CallResults>&& result) {
cannam@48 82 return result.getValue();
cannam@48 83 });
cannam@48 84 });
cannam@48 85 }
cannam@48 86
cannam@48 87 default:
cannam@48 88 // Throw an exception.
cannam@48 89 KJ_FAIL_REQUIRE("Unknown expression type.");
cannam@48 90 }
cannam@48 91 }
cannam@48 92
cannam@48 93 class ValueImpl final: public Calculator::Value::Server {
cannam@48 94 // Simple implementation of the Calculator.Value Cap'n Proto interface.
cannam@48 95
cannam@48 96 public:
cannam@48 97 ValueImpl(double value): value(value) {}
cannam@48 98
cannam@48 99 kj::Promise<void> read(ReadContext context) {
cannam@48 100 context.getResults().setValue(value);
cannam@48 101 return kj::READY_NOW;
cannam@48 102 }
cannam@48 103
cannam@48 104 private:
cannam@48 105 double value;
cannam@48 106 };
cannam@48 107
cannam@48 108 class FunctionImpl final: public Calculator::Function::Server {
cannam@48 109 // Implementation of the Calculator.Function Cap'n Proto interface, where the
cannam@48 110 // function is defined by a Calculator.Expression.
cannam@48 111
cannam@48 112 public:
cannam@48 113 FunctionImpl(uint paramCount, Calculator::Expression::Reader body)
cannam@48 114 : paramCount(paramCount) {
cannam@48 115 this->body.setRoot(body);
cannam@48 116 }
cannam@48 117
cannam@48 118 kj::Promise<void> call(CallContext context) {
cannam@48 119 auto params = context.getParams().getParams();
cannam@48 120 KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");
cannam@48 121
cannam@48 122 return evaluateImpl(body.getRoot<Calculator::Expression>(), params)
cannam@48 123 .then([context](double value) mutable {
cannam@48 124 context.getResults().setValue(value);
cannam@48 125 });
cannam@48 126 }
cannam@48 127
cannam@48 128 private:
cannam@48 129 uint paramCount;
cannam@48 130 // The function's arity.
cannam@48 131
cannam@48 132 capnp::MallocMessageBuilder body;
cannam@48 133 // Stores a permanent copy of the function body.
cannam@48 134 };
cannam@48 135
cannam@48 136 class OperatorImpl final: public Calculator::Function::Server {
cannam@48 137 // Implementation of the Calculator.Function Cap'n Proto interface, wrapping
cannam@48 138 // basic binary arithmetic operators.
cannam@48 139
cannam@48 140 public:
cannam@48 141 OperatorImpl(Calculator::Operator op): op(op) {}
cannam@48 142
cannam@48 143 kj::Promise<void> call(CallContext context) {
cannam@48 144 auto params = context.getParams().getParams();
cannam@48 145 KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
cannam@48 146
cannam@48 147 double result;
cannam@48 148 switch (op) {
cannam@48 149 case Calculator::Operator::ADD: result = params[0] + params[1]; break;
cannam@48 150 case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break;
cannam@48 151 case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break;
cannam@48 152 case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break;
cannam@48 153 default:
cannam@48 154 KJ_FAIL_REQUIRE("Unknown operator.");
cannam@48 155 }
cannam@48 156
cannam@48 157 context.getResults().setValue(result);
cannam@48 158 return kj::READY_NOW;
cannam@48 159 }
cannam@48 160
cannam@48 161 private:
cannam@48 162 Calculator::Operator op;
cannam@48 163 };
cannam@48 164
cannam@48 165 class CalculatorImpl final: public Calculator::Server {
cannam@48 166 // Implementation of the Calculator Cap'n Proto interface.
cannam@48 167
cannam@48 168 public:
cannam@48 169 kj::Promise<void> evaluate(EvaluateContext context) override {
cannam@48 170 return evaluateImpl(context.getParams().getExpression())
cannam@48 171 .then([context](double value) mutable {
cannam@48 172 context.getResults().setValue(kj::heap<ValueImpl>(value));
cannam@48 173 });
cannam@48 174 }
cannam@48 175
cannam@48 176 kj::Promise<void> defFunction(DefFunctionContext context) override {
cannam@48 177 auto params = context.getParams();
cannam@48 178 context.getResults().setFunc(kj::heap<FunctionImpl>(
cannam@48 179 params.getParamCount(), params.getBody()));
cannam@48 180 return kj::READY_NOW;
cannam@48 181 }
cannam@48 182
cannam@48 183 kj::Promise<void> getOperator(GetOperatorContext context) override {
cannam@48 184 context.getResults().setFunc(kj::heap<OperatorImpl>(
cannam@48 185 context.getParams().getOp()));
cannam@48 186 return kj::READY_NOW;
cannam@48 187 }
cannam@48 188 };
cannam@48 189
cannam@48 190 int main(int argc, const char* argv[]) {
cannam@48 191 if (argc != 2) {
cannam@48 192 std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n"
cannam@48 193 "Runs the server bound to the given address/port.\n"
cannam@48 194 "ADDRESS may be '*' to bind to all local addresses.\n"
cannam@48 195 ":PORT may be omitted to choose a port automatically." << std::endl;
cannam@48 196 return 1;
cannam@48 197 }
cannam@48 198
cannam@48 199 // Set up a server.
cannam@48 200 capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]);
cannam@48 201
cannam@48 202 // Write the port number to stdout, in case it was chosen automatically.
cannam@48 203 auto& waitScope = server.getWaitScope();
cannam@48 204 uint port = server.getPort().wait(waitScope);
cannam@48 205 if (port == 0) {
cannam@48 206 // The address format "unix:/path/to/socket" opens a unix domain socket,
cannam@48 207 // in which case the port will be zero.
cannam@48 208 std::cout << "Listening on Unix socket..." << std::endl;
cannam@48 209 } else {
cannam@48 210 std::cout << "Listening on port " << port << "..." << std::endl;
cannam@48 211 }
cannam@48 212
cannam@48 213 // Run forever, accepting connections and handling requests.
cannam@48 214 kj::NEVER_DONE.wait(waitScope);
cannam@48 215 }