calculator-server.c++ (7545B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #include "calculator.capnp.h" 23 #include <kj/debug.h> 24 #include <capnp/ez-rpc.h> 25 #include <capnp/message.h> 26 #include <iostream> 27 28 typedef unsigned int uint; 29 30 kj::Promise<double> readValue(Calculator::Value::Client value) { 31 // Helper function to asynchronously call read() on a Calculator::Value and 32 // return a promise for the result. (In the future, the generated code might 33 // include something like this automatically.) 34 35 return value.readRequest().send() 36 .then([](capnp::Response<Calculator::Value::ReadResults> result) { 37 return result.getValue(); 38 }); 39 } 40 41 kj::Promise<double> evaluateImpl( 42 Calculator::Expression::Reader expression, 43 capnp::List<double>::Reader params = capnp::List<double>::Reader()) { 44 // Implementation of CalculatorImpl::evaluate(), also shared by 45 // FunctionImpl::call(). In the latter case, `params` are the parameter 46 // values passed to the function; in the former case, `params` is just an 47 // empty list. 48 49 switch (expression.which()) { 50 case Calculator::Expression::LITERAL: 51 return expression.getLiteral(); 52 53 case Calculator::Expression::PREVIOUS_RESULT: 54 return readValue(expression.getPreviousResult()); 55 56 case Calculator::Expression::PARAMETER: { 57 KJ_REQUIRE(expression.getParameter() < params.size(), 58 "Parameter index out-of-range."); 59 return params[expression.getParameter()]; 60 } 61 62 case Calculator::Expression::CALL: { 63 auto call = expression.getCall(); 64 auto func = call.getFunction(); 65 66 // Evaluate each parameter. 67 kj::Array<kj::Promise<double>> paramPromises = 68 KJ_MAP(param, call.getParams()) { 69 return evaluateImpl(param, params); 70 }; 71 72 // Join the array of promises into a promise for an array. 73 kj::Promise<kj::Array<double>> joinedParams = 74 kj::joinPromises(kj::mv(paramPromises)); 75 76 // When the parameters are complete, call the function. 77 return joinedParams.then([KJ_CPCAP(func)](kj::Array<double>&& paramValues) mutable { 78 auto request = func.callRequest(); 79 request.setParams(paramValues); 80 return request.send().then( 81 [](capnp::Response<Calculator::Function::CallResults>&& result) { 82 return result.getValue(); 83 }); 84 }); 85 } 86 87 default: 88 // Throw an exception. 89 KJ_FAIL_REQUIRE("Unknown expression type."); 90 } 91 } 92 93 class ValueImpl final: public Calculator::Value::Server { 94 // Simple implementation of the Calculator.Value Cap'n Proto interface. 95 96 public: 97 ValueImpl(double value): value(value) {} 98 99 kj::Promise<void> read(ReadContext context) { 100 context.getResults().setValue(value); 101 return kj::READY_NOW; 102 } 103 104 private: 105 double value; 106 }; 107 108 class FunctionImpl final: public Calculator::Function::Server { 109 // Implementation of the Calculator.Function Cap'n Proto interface, where the 110 // function is defined by a Calculator.Expression. 111 112 public: 113 FunctionImpl(uint paramCount, Calculator::Expression::Reader body) 114 : paramCount(paramCount) { 115 this->body.setRoot(body); 116 } 117 118 kj::Promise<void> call(CallContext context) { 119 auto params = context.getParams().getParams(); 120 KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters."); 121 122 return evaluateImpl(body.getRoot<Calculator::Expression>(), params) 123 .then([KJ_CPCAP(context)](double value) mutable { 124 context.getResults().setValue(value); 125 }); 126 } 127 128 private: 129 uint paramCount; 130 // The function's arity. 131 132 capnp::MallocMessageBuilder body; 133 // Stores a permanent copy of the function body. 134 }; 135 136 class OperatorImpl final: public Calculator::Function::Server { 137 // Implementation of the Calculator.Function Cap'n Proto interface, wrapping 138 // basic binary arithmetic operators. 139 140 public: 141 OperatorImpl(Calculator::Operator op): op(op) {} 142 143 kj::Promise<void> call(CallContext context) { 144 auto params = context.getParams().getParams(); 145 KJ_REQUIRE(params.size() == 2, "Wrong number of parameters."); 146 147 double result; 148 switch (op) { 149 case Calculator::Operator::ADD: result = params[0] + params[1]; break; 150 case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break; 151 case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break; 152 case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break; 153 default: 154 KJ_FAIL_REQUIRE("Unknown operator."); 155 } 156 157 context.getResults().setValue(result); 158 return kj::READY_NOW; 159 } 160 161 private: 162 Calculator::Operator op; 163 }; 164 165 class CalculatorImpl final: public Calculator::Server { 166 // Implementation of the Calculator Cap'n Proto interface. 167 168 public: 169 kj::Promise<void> evaluate(EvaluateContext context) override { 170 return evaluateImpl(context.getParams().getExpression()) 171 .then([KJ_CPCAP(context)](double value) mutable { 172 context.getResults().setValue(kj::heap<ValueImpl>(value)); 173 }); 174 } 175 176 kj::Promise<void> defFunction(DefFunctionContext context) override { 177 auto params = context.getParams(); 178 context.getResults().setFunc(kj::heap<FunctionImpl>( 179 params.getParamCount(), params.getBody())); 180 return kj::READY_NOW; 181 } 182 183 kj::Promise<void> getOperator(GetOperatorContext context) override { 184 context.getResults().setFunc(kj::heap<OperatorImpl>( 185 context.getParams().getOp())); 186 return kj::READY_NOW; 187 } 188 }; 189 190 int main(int argc, const char* argv[]) { 191 if (argc != 2) { 192 std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n" 193 "Runs the server bound to the given address/port.\n" 194 "ADDRESS may be '*' to bind to all local addresses.\n" 195 ":PORT may be omitted to choose a port automatically." << std::endl; 196 return 1; 197 } 198 199 // Set up a server. 200 capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]); 201 202 // Write the port number to stdout, in case it was chosen automatically. 203 auto& waitScope = server.getWaitScope(); 204 uint port = server.getPort().wait(waitScope); 205 if (port == 0) { 206 // The address format "unix:/path/to/socket" opens a unix domain socket, 207 // in which case the port will be zero. 208 std::cout << "Listening on Unix socket..." << std::endl; 209 } else { 210 std::cout << "Listening on port " << port << "..." << std::endl; 211 } 212 213 // Run forever, accepting connections and handling requests. 214 kj::NEVER_DONE.wait(waitScope); 215 }