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