Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
gen.cpp
Go to the documentation of this file.
1// Copyright (c) The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <mp/config.h>
6#include <mp/util.h>
7
8#include <algorithm>
9#include <capnp/schema.capnp.h> // IWYU pragma: keep
10#include <capnp/schema.h>
11#include <capnp/schema-parser.h>
12#include <cstdint>
13#include <cstdio>
14#include <cstdlib>
15#include <fstream>
16#include <functional>
17#include <initializer_list>
18#include <iostream>
19#include <kj/array.h>
20#include <kj/common.h>
21#include <kj/filesystem.h>
22#include <kj/memory.h>
23#include <kj/exception.h>
24#include <kj/string.h>
25#include <map>
26#include <set>
27#include <sstream>
28#include <stdexcept>
29#include <string>
30#include <utility>
31#include <vector>
32
33#define PROXY_BIN "mpgen"
34#define PROXY_DECL "mp/proxy.h"
35#define PROXY_TYPES "mp/proxy-types.h"
36
37constexpr uint64_t NAMESPACE_ANNOTATION_ID = 0xb9c6f99ebf805f2cull; // From c++.capnp
38constexpr uint64_t INCLUDE_ANNOTATION_ID = 0xb899f3c154fdb458ull; // From proxy.capnp
39constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID = 0xbcec15648e8a0cf1ull; // From proxy.capnp
40constexpr uint64_t WRAP_ANNOTATION_ID = 0xe6f46079b7b1405eull; // From proxy.capnp
41constexpr uint64_t COUNT_ANNOTATION_ID = 0xd02682b319f69b38ull; // From proxy.capnp
42constexpr uint64_t EXCEPTION_ANNOTATION_ID = 0x996a183200992f88ull; // From proxy.capnp
43constexpr uint64_t NAME_ANNOTATION_ID = 0xb594888f63f4dbb9ull; // From proxy.capnp
44constexpr uint64_t SKIP_ANNOTATION_ID = 0x824c08b82695d8ddull; // From proxy.capnp
45
46template <typename Reader>
47static bool AnnotationExists(const Reader& reader, uint64_t id)
48{
49 for (const auto annotation : reader.getAnnotations()) {
50 if (annotation.getId() == id) {
51 return true;
52 }
53 }
54 return false;
55}
56
57template <typename Reader>
58static bool GetAnnotationText(const Reader& reader, uint64_t id, kj::StringPtr* result)
59{
60 for (const auto annotation : reader.getAnnotations()) {
61 if (annotation.getId() == id) {
62 *result = annotation.getValue().getText();
63 return true;
64 }
65 }
66 return false;
67}
68
69template <typename Reader>
70static bool GetAnnotationInt32(const Reader& reader, uint64_t id, int32_t* result)
71{
72 for (const auto annotation : reader.getAnnotations()) {
73 if (annotation.getId() == id) {
74 *result = annotation.getValue().getInt32();
75 return true;
76 }
77 }
78 return false;
79}
80
81static void ForEachMethod(const capnp::InterfaceSchema& interface, const std::function<void(const capnp::InterfaceSchema& interface, const capnp::InterfaceSchema::Method)>& callback) // NOLINT(misc-no-recursion)
82{
83 for (const auto super : interface.getSuperclasses()) {
84 ForEachMethod(super, callback);
85 }
86 for (const auto method : interface.getMethods()) {
87 callback(interface, method);
88 }
89}
90
91using CharSlice = kj::ArrayPtr<const char>;
92
93// Overload for any type with a string .begin(), like kj::StringPtr and kj::ArrayPtr<char>.
96{
97 os.write(array.begin(), array.size());
98 return os;
99}
100
101struct Format
102{
103 template <typename Value>
104 Format& operator<<(Value&& value)
105 {
106 m_os << value;
107 return *this;
108 }
109 operator std::string() const { return m_os.str(); }
110 std::ostringstream m_os;
111};
112
113static std::string Cap(kj::StringPtr str)
114{
115 std::string result = str;
116 if (!result.empty() && 'a' <= result[0] && result[0] <= 'z') result[0] -= 'a' - 'A';
117 return result;
118}
119
120static bool BoxedType(const ::capnp::Type& type)
121{
122 return !(type.isVoid() || type.isBool() || type.isInt8() || type.isInt16() || type.isInt32() || type.isInt64() ||
123 type.isUInt8() || type.isUInt16() || type.isUInt32() || type.isUInt64() || type.isFloat32() ||
124 type.isFloat64() || type.isEnum());
125}
126
127struct Field
128{
129 ::capnp::StructSchema::Field param;
130 bool param_is_set = false;
131 ::capnp::StructSchema::Field result;
132 bool result_is_set = false;
133 int args = 0;
134 bool retval = false;
135 bool optional = false;
136 bool requested = false;
137 bool skip = false;
138 kj::StringPtr exception;
139};
140
142{
143 std::vector<Field> fields;
144 std::map<kj::StringPtr, int> field_idx; // name -> args index
145 bool has_result = false;
146
147 void addField(const ::capnp::StructSchema::Field& schema_field, bool param, bool result)
148 {
149 auto field_name = schema_field.getProto().getName();
150 auto inserted = field_idx.emplace(field_name, fields.size());
151 if (inserted.second) {
152 fields.emplace_back();
153 }
154 auto& field = fields[inserted.first->second];
155 if (param) {
156 field.param = schema_field;
157 field.param_is_set = true;
158 }
159 if (result) {
160 field.result = schema_field;
161 field.result_is_set = true;
162 }
163
164 if (!param && field_name == kj::StringPtr{"result"}) {
165 field.retval = true;
166 has_result = true;
167 }
168
170 field.skip = true;
171 }
173
174 int32_t count = 1;
176 if (schema_field.getType().isStruct()) {
177 GetAnnotationInt32(schema_field.getType().asStruct().getProto(),
179 } else if (schema_field.getType().isInterface()) {
180 GetAnnotationInt32(schema_field.getType().asInterface().getProto(),
182 }
183 }
184
185
186 if (inserted.second && !field.retval && !field.exception.size()) {
187 field.args = count;
188 }
189 }
190
192 {
193 for (auto& field : field_idx) {
194 auto has_field = field_idx.find("has" + Cap(field.first));
195 if (has_field != field_idx.end()) {
196 fields[has_field->second].skip = true;
197 fields[field.second].optional = true;
198 }
199 auto want_field = field_idx.find("want" + Cap(field.first));
200 if (want_field != field_idx.end() && fields[want_field->second].param_is_set) {
201 fields[want_field->second].skip = true;
202 fields[field.second].requested = true;
203 }
204 }
205 }
206};
207
208static std::string AccessorType(kj::StringPtr base_name, const Field& field)
209{
210 const auto& f = field.param_is_set ? field.param : field.result;
211 const auto field_name = f.getProto().getName();
212 const auto field_type = f.getType();
213
214 std::ostringstream out;
215 out << "Accessor<" << base_name << "_fields::" << Cap(field_name) << ", ";
216 if (!field.param_is_set) {
217 out << "FIELD_OUT";
218 } else if (field.result_is_set) {
219 out << "FIELD_IN | FIELD_OUT";
220 } else {
221 out << "FIELD_IN";
222 }
223 if (field.optional) out << " | FIELD_OPTIONAL";
224 if (field.requested) out << " | FIELD_REQUESTED";
225 if (BoxedType(field_type)) out << " | FIELD_BOXED";
226 out << ">";
227 return out.str();
228}
229
230// src_file is path to .capnp file to generate stub code from.
231//
232// src_prefix can be used to generate outputs in a different directory than the
233// source directory. For example if src_file is "/a/b/c/d/file.canp", and
234// src_prefix is "/a/b", then output files will be "c/d/file.capnp.h"
235// "c/d/file.capnp.cxx" "c/d/file.capnp.proxy.h", etc. This is equivalent to
236// the capnp "--src-prefix" option (see "capnp help compile").
237//
238// include_prefix can be used to control relative include paths used in
239// generated files. For example if src_file is "/a/b/c/d/file.canp" and
240// include_prefix is "/a/b/c" include lines like
241// "#include <d/file.capnp.proxy.h>", "#include <d/file.capnp.proxy-types.h>"
242// will be generated.
243static void Generate(kj::StringPtr src_prefix,
244 kj::StringPtr include_prefix,
245 kj::StringPtr src_file,
246 const std::vector<kj::StringPtr>& import_paths,
247 const kj::ReadableDirectory& src_dir,
248 const std::vector<kj::Own<const kj::ReadableDirectory>>& import_dirs)
249{
250 std::string output_path;
251 if (src_prefix == kj::StringPtr{"."}) {
253 } else if (!src_file.startsWith(src_prefix) || src_file.size() <= src_prefix.size() ||
254 src_file[src_prefix.size()] != '/') {
255 throw std::runtime_error("src_prefix is not src_file prefix");
256 } else {
257 output_path = src_file.slice(src_prefix.size() + 1);
258 }
259
260 std::string include_path;
261 if (include_prefix == kj::StringPtr{"."}) {
263 } else if (!src_file.startsWith(include_prefix) || src_file.size() <= include_prefix.size() ||
264 src_file[include_prefix.size()] != '/') {
265 throw std::runtime_error("include_prefix is not src_file prefix");
266 } else {
267 include_path = src_file.slice(include_prefix.size() + 1);
268 }
269
270 std::string include_base = include_path;
271 const std::string::size_type p = include_base.rfind('.');
272 if (p != std::string::npos) include_base.erase(p);
273
274 std::vector<std::string> args;
275 args.emplace_back(CAPNP_EXECUTABLE);
276 args.emplace_back("compile");
277 args.emplace_back("--src-prefix=");
278 args.back().append(src_prefix.cStr(), src_prefix.size());
279 for (const auto& import_path : import_paths) {
280 args.emplace_back("--import-path=");
281 args.back().append(import_path.cStr(), import_path.size());
282 }
283 args.emplace_back("--output=" CAPNPC_CXX_EXECUTABLE);
284 args.emplace_back(src_file);
285 const int status = mp::WaitProcess(mp::StartProcess(args));
286 if (status) {
287 throw std::runtime_error("Invoking " CAPNP_EXECUTABLE " failed");
288 }
289
290 const capnp::SchemaParser parser;
291 auto directory_pointers = kj::heapArray<const kj::ReadableDirectory*>(import_dirs.size());
292 for (size_t i = 0; i < import_dirs.size(); ++i) {
293 directory_pointers[i] = import_dirs[i].get();
294 }
295 auto file_schema = parser.parseFromDirectory(src_dir, kj::Path::parse(output_path), directory_pointers);
296
297 std::ofstream cpp_server(output_path + ".proxy-server.c++");
298 cpp_server << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
299 cpp_server << "// IWYU pragma: no_include <kj/memory.h>\n";
300 cpp_server << "// IWYU pragma: no_include <memory>\n";
301 cpp_server << "// IWYU pragma: begin_keep\n";
302 cpp_server << "#include <" << include_path << ".proxy.h>\n";
303 cpp_server << "#include <" << include_path << ".proxy-types.h>\n";
304 cpp_server << "#include <capnp/generated-header-support.h>\n";
305 cpp_server << "#include <cstring>\n";
306 cpp_server << "#include <kj/async.h>\n";
307 cpp_server << "#include <kj/common.h>\n";
308 cpp_server << "#include <kj/exception.h>\n";
309 cpp_server << "#include <kj/tuple.h>\n";
310 cpp_server << "#include <mp/proxy.h>\n";
311 cpp_server << "#include <mp/util.h>\n";
312 cpp_server << "#include <" << PROXY_TYPES << ">\n";
313 cpp_server << "// IWYU pragma: end_keep\n\n";
314 cpp_server << "namespace mp {\n";
315
316 std::ofstream cpp_client(output_path + ".proxy-client.c++");
317 cpp_client << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
318 cpp_client << "// IWYU pragma: no_include <kj/memory.h>\n";
319 cpp_client << "// IWYU pragma: no_include <memory>\n";
320 cpp_client << "// IWYU pragma: begin_keep\n";
321 cpp_client << "#include <" << include_path << ".h>\n";
322 cpp_client << "#include <" << include_path << ".proxy.h>\n";
323 cpp_client << "#include <" << include_path << ".proxy-types.h>\n";
324 cpp_client << "#include <capnp/capability.h>\n";
325 cpp_client << "#include <capnp/common.h>\n";
326 cpp_client << "#include <capnp/generated-header-support.h>\n";
327 cpp_client << "#include <cstring>\n";
328 cpp_client << "#include <functional>\n";
329 cpp_client << "#include <kj/common.h>\n";
330 cpp_client << "#include <map>\n";
331 cpp_client << "#include <mp/proxy.h>\n";
332 cpp_client << "#include <mp/proxy-io.h>\n";
333 cpp_client << "#include <mp/util.h>\n";
334 cpp_client << "#include <string>\n";
335 cpp_client << "#include <vector>\n";
336 cpp_client << "#include <" << PROXY_TYPES << ">\n";
337 cpp_client << "// IWYU pragma: end_keep\n\n";
338 cpp_client << "namespace mp {\n";
339
340 std::ofstream cpp_types(output_path + ".proxy-types.c++");
341 cpp_types << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
342 cpp_types << "// IWYU pragma: no_include \"mp/proxy.h\"\n";
343 cpp_types << "// IWYU pragma: no_include \"mp/proxy-io.h\"\n";
344 cpp_types << "#include <" << include_path << ".h> // IWYU pragma: keep\n";
345 cpp_types << "#include <" << include_path << ".proxy.h>\n";
346 cpp_types << "#include <" << include_path << ".proxy-types.h> // IWYU pragma: keep\n";
347 cpp_types << "#include <" << PROXY_TYPES << ">\n\n";
348 cpp_types << "namespace mp {\n";
349
350 std::string guard = output_path;
351 std::ranges::transform(guard, guard.begin(), [](unsigned char c) -> unsigned char {
352 if ('0' <= c && c <= '9') return c;
353 if ('A' <= c && c <= 'Z') return c;
354 if ('a' <= c && c <= 'z') return c - 'a' + 'A';
355 return '_';
356 });
357
358 std::ofstream inl(output_path + ".proxy-types.h");
359 inl << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
360 inl << "#ifndef " << guard << "_PROXY_TYPES_H\n";
361 inl << "#define " << guard << "_PROXY_TYPES_H\n\n";
362 inl << "// IWYU pragma: no_include \"mp/proxy.h\"\n";
363 inl << "#include <mp/proxy.h> // IWYU pragma: keep\n";
364 inl << "#include <" << include_path << ".proxy.h> // IWYU pragma: keep\n";
365 for (const auto annotation : file_schema.getProto().getAnnotations()) {
367 inl << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n";
368 }
369 }
370 inl << "namespace mp {\n";
371
372 std::ofstream h(output_path + ".proxy.h");
373 h << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
374 h << "#ifndef " << guard << "_PROXY_H\n";
375 h << "#define " << guard << "_PROXY_H\n\n";
376 h << "#include <" << include_path << ".h> // IWYU pragma: keep\n";
377 for (const auto annotation : file_schema.getProto().getAnnotations()) {
378 if (annotation.getId() == INCLUDE_ANNOTATION_ID) {
379 h << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n";
380 }
381 }
382 h << "#include <" << PROXY_DECL << ">\n\n";
383 h << "#if defined(__GNUC__)\n";
384 h << "#pragma GCC diagnostic push\n";
385 h << "#if !defined(__has_warning)\n";
386 h << "#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
387 h << "#elif __has_warning(\"-Wsuggest-override\")\n";
388 h << "#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
389 h << "#endif\n";
390 h << "#endif\n";
391 h << "namespace mp {\n";
392
393 kj::StringPtr message_namespace;
395
396 std::string base_name = include_base;
397 const size_t output_slash = base_name.rfind('/');
398 if (output_slash != std::string::npos) {
399 base_name.erase(0, output_slash + 1);
400 }
401
402 std::ostringstream methods;
403 std::set<kj::StringPtr> accessors_done;
404 std::ostringstream accessors;
405 std::ostringstream dec;
406 std::ostringstream def_server;
407 std::ostringstream def_client;
408 std::ostringstream int_client;
409 std::ostringstream def_types;
410
411 auto add_accessor = [&](kj::StringPtr name) {
412 if (!accessors_done.insert(name).second) return;
413 const std::string cap = Cap(name);
414 accessors << "struct " << cap << "\n";
415 accessors << "{\n";
416 accessors << " template<typename S> static auto get(S&& s) -> decltype(s.get" << cap << "()) { return s.get" << cap << "(); }\n";
417 accessors << " template<typename S> static bool has(S&& s) { return s.has" << cap << "(); }\n";
418 accessors << " template<typename S, typename A> static void set(S&& s, A&& a) { s.set" << cap
419 << "(std::forward<A>(a)); }\n";
420 accessors << " template<typename S, typename... A> static decltype(auto) init(S&& s, A&&... a) { return s.init"
421 << cap << "(std::forward<A>(a)...); }\n";
422 accessors << " template<typename S> static bool getWant(S&& s) { return s.getWant" << cap << "(); }\n";
423 accessors << " template<typename S> static void setWant(S&& s) { s.setWant" << cap << "(true); }\n";
424 accessors << " template<typename S> static bool getHas(S&& s) { return s.getHas" << cap << "(); }\n";
425 accessors << " template<typename S> static void setHas(S&& s) { s.setHas" << cap << "(true); }\n";
426 accessors << "};\n";
427 };
428
429 for (const auto node_nested : file_schema.getProto().getNestedNodes()) {
430 kj::StringPtr node_name = node_nested.getName();
431 const auto& node = file_schema.getNested(node_name);
432 kj::StringPtr proxied_class_type;
434
435 if (node.getProto().isStruct()) {
436 const auto& struc = node.asStruct();
437
438 FieldList fields;
439 for (const auto schema_field : struc.getFields()) {
440 fields.addField(schema_field, true, true);
441 }
442 fields.mergeFields();
443
444 std::ostringstream generic_name;
446 dec << "template<";
447 bool first_param = true;
448 for (const auto param : node.getProto().getParameters()) {
449 if (first_param) {
450 first_param = false;
451 generic_name << "<";
452 } else {
453 dec << ", ";
454 generic_name << ", ";
455 }
456 dec << "typename " << param.getName();
457 generic_name << "" << param.getName();
458 }
459 if (!first_param) generic_name << ">";
460 dec << ">\n";
461 dec << "struct ProxyStruct<" << message_namespace << "::" << generic_name.str() << ">\n";
462 dec << "{\n";
463 dec << " using Struct = " << message_namespace << "::" << generic_name.str() << ";\n";
464 for (const auto& field : fields.fields) {
465 auto field_name = field.param.getProto().getName();
467 dec << " using " << Cap(field_name) << "Accessor = "
468 << AccessorType(base_name, field) << ";\n";
469 }
470 dec << " using Accessors = std::tuple<";
471 size_t i = 0;
472 for (const auto& field : fields.fields) {
473 if (field.skip) continue;
474 if (i) dec << ", ";
475 dec << Cap(field.param.getProto().getName()) << "Accessor";
476 ++i;
477 }
478 dec << ">;\n";
479 dec << " static constexpr size_t fields = " << i << ";\n";
480 dec << "};\n";
481
482 if (proxied_class_type.size()) {
483 inl << "template<>\n";
484 inl << "struct ProxyType<" << proxied_class_type << ">\n";
485 inl << "{\n";
486 inl << "public:\n";
487 inl << " using Struct = " << message_namespace << "::" << node_name << ";\n";
488 size_t i = 0;
489 for (const auto& field : fields.fields) {
490 if (field.skip) continue;
491 auto field_name = field.param.getProto().getName();
492 auto member_name = field_name;
494 inl << " static decltype(auto) get(std::integral_constant<size_t, " << i << ">) { return "
495 << "&" << proxied_class_type << "::" << member_name << "; }\n";
496 ++i;
497 }
498 inl << " static constexpr size_t fields = " << i << ";\n";
499 inl << "};\n";
500 }
501 }
502
503 if (proxied_class_type.size() && node.getProto().isInterface()) {
504 const auto& interface = node.asInterface();
505
506 std::ostringstream client;
507 client << "template<>\nstruct ProxyClient<" << message_namespace << "::" << node_name << "> final : ";
508 client << "public ProxyClientCustom<" << message_namespace << "::" << node_name << ", "
509 << proxied_class_type << ">\n{\n";
510 client << "public:\n";
511 client << " using ProxyClientCustom::ProxyClientCustom;\n";
512 client << " ~ProxyClient();\n";
513
514 std::ostringstream server;
515 server << "template<>\nstruct ProxyServer<" << message_namespace << "::" << node_name << "> : public "
516 << "ProxyServerCustom<" << message_namespace << "::" << node_name << ", " << proxied_class_type
517 << ">\n{\n";
518 server << "public:\n";
519 server << " using ProxyServerCustom::ProxyServerCustom;\n";
520 server << " ~ProxyServer();\n";
521
522 const std::ostringstream client_construct;
523 const std::ostringstream client_destroy;
524
525 int method_ordinal = 0;
526 ForEachMethod(interface, [&] (const capnp::InterfaceSchema& method_interface, const capnp::InterfaceSchema::Method& method) {
527 const kj::StringPtr method_name = method.getProto().getName();
528 kj::StringPtr proxied_method_name = method_name;
530
531 const std::string method_prefix = Format() << message_namespace << "::" << method_interface.getShortDisplayName()
532 << "::" << Cap(method_name);
533 const bool is_construct = method_name == kj::StringPtr{"construct"};
534 const bool is_destroy = method_name == kj::StringPtr{"destroy"};
535
536 FieldList fields;
537 for (const auto schema_field : method.getParamType().getFields()) {
538 fields.addField(schema_field, true, false);
539 }
540 for (const auto schema_field : method.getResultType().getFields()) {
541 fields.addField(schema_field, false, true);
542 }
543 fields.mergeFields();
544
546 methods << "template<>\n";
547 methods << "struct ProxyMethod<" << method_prefix << "Params>\n";
548 methods << "{\n";
549 methods << " static constexpr auto impl = &" << proxied_class_type
550 << "::" << proxied_method_name << ";\n";
551 methods << "};\n\n";
552 }
553
554 std::ostringstream client_args;
555 std::ostringstream client_invoke;
556 std::ostringstream server_invoke_start;
557 std::ostringstream server_invoke_end;
558 int argc = 0;
559 for (const auto& field : fields.fields) {
560 if (field.skip) continue;
561
562 const auto& f = field.param_is_set ? field.param : field.result;
563 auto field_name = f.getProto().getName();
565
566 std::ostringstream fwd_args;
567 for (int i = 0; i < field.args; ++i) {
568 if (argc > 0) client_args << ",";
569
570 // Add to client method parameter list.
571 client_args << "M" << method_ordinal << "::Param<" << argc << "> " << field_name;
572 if (field.args > 1) client_args << i;
573
574 // Add to MakeClientParam argument list using Fwd helper for perfect forwarding.
575 if (i > 0) fwd_args << ", ";
576 fwd_args << "M" << method_ordinal << "::Fwd<" << argc << ">(" << field_name;
577 if (field.args > 1) fwd_args << i;
578 fwd_args << ")";
579
580 ++argc;
581 }
582 client_invoke << ", ";
583
584 if (field.exception.size()) {
585 client_invoke << "ClientException<" << field.exception << ", ";
586 } else {
587 client_invoke << "MakeClientParam<";
588 }
589
591
592 if (field.retval) {
594 } else {
595 client_invoke << fwd_args.str();
596 }
597 client_invoke << ")";
598
599 if (field.exception.size()) {
600 server_invoke_start << "Make<ServerExcept, " << field.exception;
601 } else if (field.retval) {
602 server_invoke_start << "Make<ServerRet";
603 } else {
604 server_invoke_start << "MakeServerField<" << field.args;
605 }
606 server_invoke_start << ", " << AccessorType(base_name, field) << ">(";
607 server_invoke_end << ")";
608 }
609
610 const std::string static_str{is_construct || is_destroy ? "static " : ""};
611 const std::string super_str{is_construct || is_destroy ? "Super& super" : ""};
612 const std::string self_str{is_construct || is_destroy ? "super" : "*this"};
613
614 client << " using M" << method_ordinal << " = ProxyClientMethodTraits<" << method_prefix
615 << "Params>;\n";
616 client << " " << static_str << "typename M" << method_ordinal << "::Result " << method_name << "("
617 << super_str << client_args.str() << ")";
618 client << ";\n";
619 def_client << "ProxyClient<" << message_namespace << "::" << node_name << ">::M" << method_ordinal
620 << "::Result ProxyClient<" << message_namespace << "::" << node_name << ">::" << method_name
621 << "(" << super_str << client_args.str() << ") {\n";
622 if (fields.has_result) {
623 def_client << " typename M" << method_ordinal << "::Result result;\n";
624 }
625 def_client << " clientInvoke(" << self_str << ", &" << message_namespace << "::" << node_name
626 << "::Client::" << method_name << "Request" << client_invoke.str() << ");\n";
627 if (fields.has_result) def_client << " return result;\n";
628 def_client << "}\n";
629
630 server << " kj::Promise<void> " << method_name << "(" << Cap(method_name)
631 << "Context call_context) override;\n";
632
633 def_server << "kj::Promise<void> ProxyServer<" << message_namespace << "::" << node_name
634 << ">::" << method_name << "(" << Cap(method_name)
635 << "Context call_context) {\n"
636 " return serverInvoke(*this, call_context, "
637 << server_invoke_start.str();
638 if (is_destroy) {
639 def_server << "ServerDestroy()";
640 } else {
641 def_server << "ServerCall()";
642 }
643 def_server << server_invoke_end.str() << ");\n}\n";
645 });
646
647 client << "};\n";
648 server << "};\n";
649 dec << "\n" << client.str() << "\n" << server.str() << "\n";
650 KJ_IF_MAYBE(bracket, proxied_class_type.findFirst('<')) {
651 // Skip ProxyType definition for complex type expressions which
652 // could lead to duplicate definitions. They can be defined
653 // manually if actually needed.
654 } else {
655 dec << "template<>\nstruct ProxyType<" << proxied_class_type << ">\n{\n";
656 dec << " using Type = " << proxied_class_type << ";\n";
657 dec << " using Message = " << message_namespace << "::" << node_name << ";\n";
658 dec << " using Client = ProxyClient<Message>;\n";
659 dec << " using Server = ProxyServer<Message>;\n";
660 dec << "};\n";
661 int_client << "ProxyTypeRegister t" << node_nested.getId() << "{TypeList<" << proxied_class_type << ">{}};\n";
662 }
663 def_types << "ProxyClient<" << message_namespace << "::" << node_name
664 << ">::~ProxyClient() { clientDestroy(*this); " << client_destroy.str() << " }\n";
665 def_types << "ProxyServer<" << message_namespace << "::" << node_name
666 << ">::~ProxyServer() { serverDestroy(*this); }\n";
667 }
668 }
669
670 h << methods.str() << "namespace " << base_name << "_fields {\n"
671 << accessors.str() << "} // namespace " << base_name << "_fields\n"
672 << dec.str();
673
674 cpp_server << def_server.str();
675 cpp_server << "} // namespace mp\n";
676
677 cpp_client << def_client.str();
678 cpp_client << "namespace {\n" << int_client.str() << "} // namespace\n";
679 cpp_client << "} // namespace mp\n";
680
681 cpp_types << def_types.str();
682 cpp_types << "} // namespace mp\n";
683
684 inl << "} // namespace mp\n";
685 inl << "#endif\n";
686
687 h << "} // namespace mp\n";
688 h << "#if defined(__GNUC__)\n";
689 h << "#pragma GCC diagnostic pop\n";
690 h << "#endif\n";
691 h << "#endif\n";
692}
693
694int main(int argc, char** argv)
695{
696 int ret = 1;
697 KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
698 if (argc < 3) {
699 std::cerr << "Usage: " << PROXY_BIN << " SRC_PREFIX INCLUDE_PREFIX SRC_FILE [IMPORT_PATH...]\n";
700 exit(1);
701 }
702 std::vector<kj::StringPtr> import_paths;
703 std::vector<kj::Own<const kj::ReadableDirectory>> import_dirs;
704 auto fs = kj::newDiskFilesystem();
705 auto cwd = fs->getCurrentPath();
706 kj::Own<const kj::ReadableDirectory> src_dir;
707 KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(argv[1]))) {
708 src_dir = kj::mv(*dir);
709 } else {
710 throw std::runtime_error(std::string("Failed to open src_prefix prefix directory: ") + argv[1]);
711 }
712 for (int i = 4; i < argc; ++i) {
713 KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(argv[i]))) {
714 import_paths.emplace_back(argv[i]);
715 import_dirs.emplace_back(kj::mv(*dir));
716 } else {
717 throw std::runtime_error(std::string("Failed to open import directory: ") + argv[i]);
718 }
719 }
720 for (const char* path : {CMAKE_INSTALL_PREFIX "/include", capnp_PREFIX "/include"}) {
721 KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(path))) {
722 import_paths.emplace_back(path);
723 import_dirs.emplace_back(kj::mv(*dir));
724 }
725 // No exception thrown if _PREFIX directories do not exist
726 }
728 ret = 0;
729 })) {
730 std::cerr << "mpgen error: " << kj::str(*exception).cStr() << '\n';
731 }
732 return ret;
733}
int ret
ArgsManager & args
Definition bitcoind.cpp:280
int main(void)
Definition bench.c:156
kj::ArrayPtr< const char > CharSlice
Definition gen.cpp:91
constexpr uint64_t EXCEPTION_ANNOTATION_ID
Definition gen.cpp:42
constexpr uint64_t NAME_ANNOTATION_ID
Definition gen.cpp:43
static OutputStream & operator<<(OutputStream &os, const Array &array)
Definition gen.cpp:95
static void ForEachMethod(const capnp::InterfaceSchema &interface, const std::function< void(const capnp::InterfaceSchema &interface, const capnp::InterfaceSchema::Method)> &callback)
Definition gen.cpp:81
constexpr uint64_t SKIP_ANNOTATION_ID
Definition gen.cpp:44
constexpr uint64_t WRAP_ANNOTATION_ID
Definition gen.cpp:40
constexpr uint64_t INCLUDE_ANNOTATION_ID
Definition gen.cpp:38
static void Generate(kj::StringPtr src_prefix, kj::StringPtr include_prefix, kj::StringPtr src_file, const std::vector< kj::StringPtr > &import_paths, const kj::ReadableDirectory &src_dir, const std::vector< kj::Own< const kj::ReadableDirectory > > &import_dirs)
Definition gen.cpp:243
static bool GetAnnotationInt32(const Reader &reader, uint64_t id, int32_t *result)
Definition gen.cpp:70
static bool BoxedType(const ::capnp::Type &type)
Definition gen.cpp:120
#define PROXY_TYPES
Definition gen.cpp:35
#define PROXY_BIN
Definition gen.cpp:33
constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID
Definition gen.cpp:39
constexpr uint64_t NAMESPACE_ANNOTATION_ID
Definition gen.cpp:37
static bool AnnotationExists(const Reader &reader, uint64_t id)
Definition gen.cpp:47
static std::string AccessorType(kj::StringPtr base_name, const Field &field)
Definition gen.cpp:208
static std::string Cap(kj::StringPtr str)
Definition gen.cpp:113
static bool GetAnnotationText(const Reader &reader, uint64_t id, kj::StringPtr *result)
Definition gen.cpp:58
#define PROXY_DECL
Definition gen.cpp:34
constexpr uint64_t COUNT_ANNOTATION_ID
Definition gen.cpp:41
util::LineReader reader
std::unique_ptr< ProxyClient< messages::FooInterface > > client
ProcessId StartProcess(const std::vector< std::string > &args)
Start a process and return its process id.
Definition util.cpp:361
int WaitProcess(ProcessId pid)
Wait for a process to exit and return its exit code.
Definition util.cpp:371
const char * name
Definition rest.cpp:71
Definition gen.cpp:128
bool optional
Definition gen.cpp:135
bool skip
Definition gen.cpp:137
::capnp::StructSchema::Field result
Definition gen.cpp:131
int args
Definition gen.cpp:133
bool retval
Definition gen.cpp:134
::capnp::StructSchema::Field param
Definition gen.cpp:129
bool result_is_set
Definition gen.cpp:132
bool requested
Definition gen.cpp:136
bool param_is_set
Definition gen.cpp:130
kj::StringPtr exception
Definition gen.cpp:138
std::vector< Field > fields
Definition gen.cpp:143
std::map< kj::StringPtr, int > field_idx
Definition gen.cpp:144
void addField(const ::capnp::StructSchema::Field &schema_field, bool param, bool result)
Definition gen.cpp:147
void mergeFields()
Definition gen.cpp:191
bool has_result
Definition gen.cpp:145
Format & operator<<(Value &&value)
Definition gen.cpp:104
std::ostringstream m_os
Definition gen.cpp:110
static int count
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82