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