capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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 }