Mercurial > hg > sv-dependency-builds
comparison src/capnproto-git-20161025/c++/samples/calculator-server.c++ @ 48:9530b331f8c1
Add Cap'n Proto source
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Tue, 25 Oct 2016 11:17:01 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
47:d93140aac40b | 48:9530b331f8c1 |
---|---|
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([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([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([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 } |