AnDiXL commented on code in PR #3558: URL: https://github.com/apache/brpc/pull/3558#discussion_r4311618455
########## tools/flatbuffers/brpc_flatc.cpp: ########## @@ -0,0 +1,761 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <cerrno> +#include <cstdio> +#include <cstdint> +#include <cstdlib> +#include <exception> +#include <fstream> +#include <iostream> +#include <iterator> +#include <limits> +#include <set> +#include <sstream> +#include <string> +#include <vector> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> +#include <flatbuffers/idl.h> + +namespace { + +const char kLicense[] = R"(// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Generated by brpc_flatc. Do not edit. + +)"; + +const char kArguments[] = + "::google::protobuf::RpcController* controller,\n" + " const ::brpc::flatbuffers::Message* request,\n" + " ::brpc::flatbuffers::Message* response,\n" + " ::google::protobuf::Closure* done"; + +struct Service { + const flatbuffers::ServiceDef* definition; + std::vector<int32_t> ids; +}; + +std::string JoinNamespace(const flatbuffers::Definition& definition, + const std::string& separator) { + std::string result; + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + if (!result.empty()) { + result += separator; + } + result += component; + } + } + return result; +} + +std::string Qualified(const flatbuffers::Definition& definition) { + const std::string ns = JoinNamespace(definition, "::"); + return "::" + (ns.empty() ? "" : ns + "::") + definition.name; +} + +std::string GuardComponent(const std::string& value) { + static const char digits[] = "0123456789ABCDEF"; + std::string result; + for (unsigned char c : value) { + result += digits[c >> 4]; + result += digits[c & 15]; + } + return result; +} + +bool IsCppIdentifier(const std::string& name) { + static const std::set<std::string> keywords = { + "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", + "bitor", "bool", "break", "case", "catch", "char", "char8_t", + "char16_t", "char32_t", "class", "compl", "concept", "const", + "const_cast", "consteval", "constexpr", "constinit", "continue", + "co_await", "co_return", "co_yield", "decltype", "default", + "delete", "do", + "double", "dynamic_cast", "else", "enum", "explicit", "export", + "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", + "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", + "private", "protected", "public", "register", "reinterpret_cast", + "requires", "return", "short", "signed", "sizeof", "static", + "static_assert", "static_cast", "struct", "switch", "template", + "this", "thread_local", "throw", "true", "try", "typedef", + "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + return !name.empty() && keywords.count(name) == 0; +} + +bool ValidateName(const flatbuffers::Definition& definition, + std::string* error) { + if (!IsCppIdentifier(definition.name)) { + *error = "C++ keyword is not supported: " + definition.name; + return false; + } + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + if (!IsCppIdentifier(component)) { + *error = "C++ keyword namespace is not supported: " + component; + return false; + } + } + } + return true; +} + +bool ParseId(const flatbuffers::Value& value, int32_t* id) { + if (value.constant.empty() || + !flatbuffers::IsInteger(value.type.base_type)) { + return false; + } + int64_t number = 0; + for (char c : value.constant) { + if (c < '0' || c > '9') { + return false; + } + number = number * 10 + (c - '0'); + if (number > std::numeric_limits<int32_t>::max()) { + return false; + } + } + *id = static_cast<int32_t>(number); + return true; +} + +bool CollectServices(const flatbuffers::Parser& parser, + std::vector<Service>* services, std::string* error) { + std::set<std::string> generated_class_names = { + "::brpc", "::butil", "::flatbuffers", "::google", "::std" + }; + // Reserve every namespace prefix, including those from imported schemas. + for (const auto* ns : parser.namespaces_) { + std::string prefix; + for (const auto& component : ns->components) { + prefix += "::" + component; + generated_class_names.insert(prefix); + } + } + for (const auto* definition : parser.services_.vec) { + if (definition->generated) { + const std::string service_name = Qualified(*definition); + generated_class_names.insert(service_name); + generated_class_names.insert(service_name + "_Stub"); + } + } + for (const auto* definition : parser.structs_.vec) { + const std::string type_name = Qualified(*definition); + generated_class_names.insert(type_name); + if (!definition->fixed) { + generated_class_names.insert(type_name + "Builder"); + } + } + for (const auto* definition : parser.enums_.vec) { + generated_class_names.insert(Qualified(*definition)); + } Review Comment: Good catch, thanks! Unscoped enumerators are now part of the collision check, and the `Echo_Stub = 0` case is in the acceptance tests. Fixed in 60550a3d. ########## tools/flatbuffers/brpc_flatc.cpp: ########## @@ -0,0 +1,761 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <cerrno> +#include <cstdio> +#include <cstdint> +#include <cstdlib> +#include <exception> +#include <fstream> +#include <iostream> +#include <iterator> +#include <limits> +#include <set> +#include <sstream> +#include <string> +#include <vector> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> +#include <flatbuffers/idl.h> + +namespace { + +const char kLicense[] = R"(// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Generated by brpc_flatc. Do not edit. + +)"; + +const char kArguments[] = + "::google::protobuf::RpcController* controller,\n" + " const ::brpc::flatbuffers::Message* request,\n" + " ::brpc::flatbuffers::Message* response,\n" + " ::google::protobuf::Closure* done"; + +struct Service { + const flatbuffers::ServiceDef* definition; + std::vector<int32_t> ids; +}; + +std::string JoinNamespace(const flatbuffers::Definition& definition, + const std::string& separator) { + std::string result; + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + if (!result.empty()) { + result += separator; + } + result += component; + } + } + return result; +} + +std::string Qualified(const flatbuffers::Definition& definition) { + const std::string ns = JoinNamespace(definition, "::"); + return "::" + (ns.empty() ? "" : ns + "::") + definition.name; +} + +std::string GuardComponent(const std::string& value) { + static const char digits[] = "0123456789ABCDEF"; + std::string result; + for (unsigned char c : value) { + result += digits[c >> 4]; + result += digits[c & 15]; + } + return result; +} + +bool IsCppIdentifier(const std::string& name) { + static const std::set<std::string> keywords = { + "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", + "bitor", "bool", "break", "case", "catch", "char", "char8_t", + "char16_t", "char32_t", "class", "compl", "concept", "const", + "const_cast", "consteval", "constexpr", "constinit", "continue", + "co_await", "co_return", "co_yield", "decltype", "default", + "delete", "do", + "double", "dynamic_cast", "else", "enum", "explicit", "export", + "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", + "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", + "private", "protected", "public", "register", "reinterpret_cast", + "requires", "return", "short", "signed", "sizeof", "static", + "static_assert", "static_cast", "struct", "switch", "template", + "this", "thread_local", "throw", "true", "try", "typedef", + "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + return !name.empty() && keywords.count(name) == 0; +} + +bool ValidateName(const flatbuffers::Definition& definition, + std::string* error) { + if (!IsCppIdentifier(definition.name)) { + *error = "C++ keyword is not supported: " + definition.name; + return false; + } + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + if (!IsCppIdentifier(component)) { + *error = "C++ keyword namespace is not supported: " + component; + return false; + } + } + } + return true; +} + +bool ParseId(const flatbuffers::Value& value, int32_t* id) { + if (value.constant.empty() || + !flatbuffers::IsInteger(value.type.base_type)) { + return false; + } + int64_t number = 0; + for (char c : value.constant) { + if (c < '0' || c > '9') { + return false; + } + number = number * 10 + (c - '0'); + if (number > std::numeric_limits<int32_t>::max()) { + return false; + } + } + *id = static_cast<int32_t>(number); + return true; +} + +bool CollectServices(const flatbuffers::Parser& parser, + std::vector<Service>* services, std::string* error) { + std::set<std::string> generated_class_names = { + "::brpc", "::butil", "::flatbuffers", "::google", "::std" + }; + // Reserve every namespace prefix, including those from imported schemas. + for (const auto* ns : parser.namespaces_) { + std::string prefix; + for (const auto& component : ns->components) { + prefix += "::" + component; + generated_class_names.insert(prefix); + } + } + for (const auto* definition : parser.services_.vec) { + if (definition->generated) { + const std::string service_name = Qualified(*definition); + generated_class_names.insert(service_name); + generated_class_names.insert(service_name + "_Stub"); + } + } + for (const auto* definition : parser.structs_.vec) { + const std::string type_name = Qualified(*definition); + generated_class_names.insert(type_name); + if (!definition->fixed) { + generated_class_names.insert(type_name + "Builder"); + } + } + for (const auto* definition : parser.enums_.vec) { + generated_class_names.insert(Qualified(*definition)); + } + for (const auto* definition : parser.services_.vec) { + // Included schemas are generated separately, just as with flatc --cpp. + if (definition->generated) { + continue; + } + if (!ValidateName(*definition, error)) { + return false; + } + if (definition->name == "Stub" || definition->name == "descriptor" || + definition->name == "GetDescriptor" || + definition->name == "FBCallMethod") { + *error = "service name collides with generated API: " + + definition->name; + return false; + } + const std::string service_name = Qualified(*definition); + const std::string stub_name = service_name + "_Stub"; + if (!generated_class_names.insert(service_name).second) { + *error = "generated service class name collides: " + service_name; + return false; + } + if (!generated_class_names.insert(stub_name).second) { + *error = "generated service class name collides: " + stub_name; + return false; + } + Service service = {definition, {}}; + std::set<int32_t> ids; + if (definition->calls.vec.empty()) { + *error = "rpc_service must contain at least one method: " + + definition->name; + return false; + } + for (const auto* call : definition->calls.vec) { + if (!ValidateName(*call, error) || + !ValidateName(*call->request, error) || + !ValidateName(*call->response, error)) { + return false; + } + if (call->name == definition->name || + call->name == definition->name + "_Stub" || + call->name == "descriptor" || call->name == "GetDescriptor" || + call->name == "FBCallMethod" || call->name == "Stub" || + call->name == "channel" || call->name == "channel_" || + call->name == "owned_channel_") { + *error = "method name collides with generated API: " + + call->name; + return false; + } + const auto* attribute = call->attributes.Lookup("id"); + int32_t id = 0; + if (!attribute || !ParseId(*attribute, &id)) { + *error = definition->name + "." + call->name + + " requires an explicit nonnegative int32 (id: N)"; + return false; + } + if (!ids.insert(id).second) { + *error = "duplicate method id " + std::to_string(id) + + " in " + definition->name; + return false; + } + if (call->attributes.Lookup("streaming")) { + *error = "streaming RPC is not supported: " + call->name; + return false; + } + service.ids.push_back(id); + } + services->push_back(service); + } + if (services->empty()) { + *error = "input schema contains no rpc_service to generate"; + return false; + } + return true; +} + +void OpenNamespace(const flatbuffers::Definition& definition, + std::ostream& out) { + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + out << "namespace " << component << " {\n"; + } + } + out << '\n'; +} + +void CloseNamespace(const flatbuffers::Definition& definition, + std::ostream& out) { + if (definition.defined_namespace) { + const auto& components = definition.defined_namespace->components; + for (auto it = components.rbegin(); it != components.rend(); ++it) { + out << "} // namespace " << *it << '\n'; + } + } + out << '\n'; +} + +void GenerateHeader(const Service& service, std::ostream& out) { + const auto& definition = *service.definition; + const std::string& name = definition.name; + OpenNamespace(definition, out); + out << "class " << name << "_Stub;\n\n" + << "class " << name << " : public ::brpc::flatbuffers::Service {\n" + << "public:\n" + << " typedef " << name << "_Stub Stub;\n" + << " static const ::brpc::flatbuffers::ServiceDescriptor* " + "descriptor();\n" + << " const ::brpc::flatbuffers::ServiceDescriptor* " + "GetDescriptor() override;\n" + << " void FBCallMethod(\n" + << " const ::brpc::flatbuffers::MethodDescriptor* method,\n" + << " " << kArguments << ") override;\n"; + for (const auto* call : definition.calls.vec) { + out << " virtual void " << call->name << "(\n" + << " " << kArguments << ");\n"; + } + out << "};\n\n" + << "class " << name << "_Stub : public " << name << " {\n" + << "public:\n" + << " explicit " << name << "_Stub(\n" + << " ::brpc::flatbuffers::RpcChannel* channel,\n" + << " ::brpc::flatbuffers::Service::ChannelOwnership " + "ownership =\n" + << " ::brpc::flatbuffers::Service::" + "STUB_DOESNT_OWN_CHANNEL);\n" + << " ::brpc::flatbuffers::RpcChannel* channel() const {\n" + << " return channel_;\n" + << " }\n"; + for (const auto* call : definition.calls.vec) { + out << " void " << call->name << "(\n" + << " " << kArguments << ") override;\n"; + } + out << "\nprivate:\n" + << " ::brpc::flatbuffers::RpcChannel* channel_;\n" + << " ::std::unique_ptr<::brpc::flatbuffers::RpcChannel> " + "owned_channel_;\n" + << "};\n\n"; + CloseNamespace(definition, out); +} + +void GenerateSource(const Service& service, std::ostream& out) { + const auto& definition = *service.definition; + const std::string& name = definition.name; + OpenNamespace(definition, out); + out << "const ::brpc::flatbuffers::ServiceDescriptor* " << name + << "::descriptor() {\n" + << " struct Holder {\n" + << " ::brpc::flatbuffers::ServiceDescriptor value;\n" + << " Holder() {\n" + << " const ::brpc::flatbuffers::BrpcDescriptorTable " + "table = {\n" + << " \"" << JoinNamespace(definition, ".") << "\", \"" + << name << "\",\n \""; + for (size_t i = 0; i < definition.calls.vec.size(); ++i) { + out << (i == 0 ? "" : " ") << definition.calls.vec[i]->name; + } + out << "\", {"; + for (size_t i = 0; i < service.ids.size(); ++i) { + out << (i == 0 ? "" : ", ") << service.ids[i]; + } + out << "}\n };\n" + << " if (value.init(table) != 0) {\n" + << " throw ::std::runtime_error(\n" + << " \"invalid generated service descriptor\");\n" + << " }\n" + << " }\n" + << " };\n" + << " static const Holder holder;\n" + << " return &holder.value;\n" + << "}\n\n" + << "const ::brpc::flatbuffers::ServiceDescriptor* " << name + << "::GetDescriptor() {\n" + << " return descriptor();\n" + << "}\n\n" + << "void " << name << "::FBCallMethod(\n" + << " const ::brpc::flatbuffers::MethodDescriptor* method,\n" + << " " << kArguments << ") {\n" + << " if (!method || method->service() != descriptor() ||\n" + << " descriptor()->FindMethodByIndex(method->index()) != " + "method) {\n" + << " ::BrpcFlatbuffersFail(controller, done,\n" + << " \"invalid service method\");\n" + << " return;\n" + << " }\n" + << " if (!request || !response) {\n" + << " ::BrpcFlatbuffersFail(controller, done,\n" + << " \"null request or response\");\n" + << " return;\n" + << " }\n" + << " switch (method->index()) {\n"; + for (size_t i = 0; i < definition.calls.vec.size(); ++i) { + const auto& call = *definition.calls.vec[i]; + out << " case " << service.ids[i] << ":\n" + << " if (!request->Verify<" << Qualified(*call.request) + << ">()) {\n" + << " ::BrpcFlatbuffersFail(controller, done, \"invalid " + << call.request->name << " request\");\n" + << " return;\n" + << " }\n" + << " this->" << call.name + << "(controller, request, response, done);\n" + << " return;\n"; + } + out << " default:\n" + << " ::BrpcFlatbuffersFail(controller, done,\n" + << " \"unknown method id\");\n" + << " return;\n" + << " }\n" + << "}\n\n"; + for (const auto* call : definition.calls.vec) { + out << "void " << name << "::" << call->name << "(\n" + << " " << kArguments << ") {\n" + << " (void)request;\n" + << " (void)response;\n" + << " ::BrpcFlatbuffersFail(controller, done,\n" + << " \"method not implemented: " + << name << "." << call->name << "\");\n" + << "}\n\n"; + } + out << name << "_Stub::" << name << "_Stub(\n" + << " ::brpc::flatbuffers::RpcChannel* channel,\n" + << " ::brpc::flatbuffers::Service::ChannelOwnership ownership)\n" + << " : channel_(channel),\n" + << " owned_channel_(\n" + << " ownership == " + "::brpc::flatbuffers::Service::STUB_OWNS_CHANNEL\n" + << " ? channel : nullptr) {}\n\n"; + for (size_t i = 0; i < definition.calls.vec.size(); ++i) { + out << "void " << name << "_Stub::" << definition.calls.vec[i]->name + << "(\n " << kArguments << ") {\n" + << " if (!channel_) {\n" + << " ::BrpcFlatbuffersFail(controller, done,\n" + << " \"null RPC channel\");\n" + << " return;\n" + << " }\n" + << " channel_->FBCallMethod(\n" + << " descriptor()->method(" << i + << "), controller, request, response, done);\n" + << "}\n\n"; + } + CloseNamespace(definition, out); +} + +bool TestFailureEnabled(const char* step) { +#if defined(BRPC_FLATC_TESTING) + const char* value = std::getenv("BRPC_FLATC_TEST_FAILURE"); + if (value) { + const std::string failures = "," + std::string(value) + ","; + return failures.find("," + std::string(step) + ",") != + std::string::npos; + } +#else + (void)step; +#endif + return false; +} + +bool WriteFile(const std::string& path, const std::string& contents, + const char* step) { + if (TestFailureEnabled(step)) { + errno = EIO; + return false; + } + std::ofstream stream(path.c_str(), std::ios::binary | std::ios::trunc); + stream << contents; + stream.close(); + return !stream.fail(); +} + +bool RenameFile(const std::string& from, const std::string& to, + const char* step) { + if (TestFailureEnabled(step)) { + errno = EIO; + return false; + } + return std::rename(from.c_str(), to.c_str()) == 0; +} + +class OutputDirectoryLock { +public: + explicit OutputDirectoryLock(const std::string& directory) + : fd_(open(directory.c_str(), O_RDONLY)) { + if (fd_ < 0) { + return; + } + while (flock(fd_, LOCK_EX) != 0) { + if (errno != EINTR) { + close(fd_); + fd_ = -1; + return; + } + } + } + + ~OutputDirectoryLock() { + if (fd_ >= 0) { + close(fd_); + } + } + + bool acquired() const { return fd_ >= 0; } + +private: + OutputDirectoryLock(const OutputDirectoryLock&) = delete; + OutputDirectoryLock& operator=(const OutputDirectoryLock&) = delete; + + int fd_; +}; + +bool RemoveFile(const std::string& path) { + return std::remove(path.c_str()) == 0 || errno == ENOENT; +} + +bool IsRegularFileOrMissing(const std::string& path) { + struct stat file_status; + if (lstat(path.c_str(), &file_status) == 0) { + return S_ISREG(file_status.st_mode); + } + return errno == ENOENT; +} + +bool MoveExistingFile(const std::string& path, const std::string& backup, + const char* step, bool* moved) { + *moved = false; + if (RenameFile(path, backup, step)) { + *moved = true; + return true; + } + return errno == ENOENT; +} + +bool RestoreFile(const std::string& path, const std::string& backup, + const char* step, bool had_original) { + if (!RemoveFile(path)) { + return false; + } + return !had_original || RenameFile(backup, path, step); +} + +bool RemoveOutputPair(const std::string& header_path, + const std::string& source_path) { + const bool header_removed = RemoveFile(header_path); + const bool source_removed = RemoveFile(source_path); + return header_removed && source_removed; +} + +bool PublishFiles(const std::string& output_dir, + const std::string& header_path, + const std::string& header_contents, + const std::string& source_path, + const std::string& source_contents, + std::string* error) { + OutputDirectoryLock lock(output_dir); + if (!lock.acquired()) { + *error = "cannot lock output directory"; + return false; + } + const std::string header_tmp = header_path + ".tmp"; + const std::string source_tmp = source_path + ".tmp"; + const std::string header_backup = header_path + ".bak"; + const std::string source_backup = source_path + ".bak"; + if (!RemoveFile(header_tmp) || + !WriteFile(header_tmp, header_contents, "stage_header")) { + RemoveFile(header_tmp); + *error = "cannot stage header output"; + return false; + } + if (!RemoveFile(source_tmp) || + !WriteFile(source_tmp, source_contents, "stage_source")) { + RemoveFile(header_tmp); + RemoveFile(source_tmp); + *error = "cannot stage source output"; + return false; + } + if (!IsRegularFileOrMissing(header_path) || + !IsRegularFileOrMissing(source_path) || + !RemoveFile(header_backup) || !RemoveFile(source_backup)) { + RemoveFile(header_tmp); + RemoveFile(source_tmp); + *error = "output or backup path is not replaceable"; + return false; + } + + bool had_header = false; + bool had_source = false; + if (!MoveExistingFile(header_path, header_backup, + "preserve_header", &had_header)) { + RemoveFile(header_tmp); + RemoveFile(source_tmp); + *error = "cannot preserve previous header"; + return false; + } + if (!MoveExistingFile(source_path, source_backup, + "preserve_source", &had_source)) { + const bool restored = RestoreFile( + header_path, header_backup, "restore_header", had_header); + RemoveFile(header_tmp); + RemoveFile(source_tmp); + if (!restored) { + const bool cleared = RemoveOutputPair(header_path, source_path); + *error = cleared + ? "cannot preserve source or restore header" + : "cannot preserve source; manual cleanup required"; + } else { + *error = "cannot preserve previous source"; + } + return false; + } + + if (!RenameFile(header_tmp, header_path, "publish_header") || + !RenameFile(source_tmp, source_path, "publish_source")) { + const bool header_restored = RestoreFile( + header_path, header_backup, "restore_header", had_header); + const bool source_restored = RestoreFile( + source_path, source_backup, "restore_source", had_source); + RemoveFile(header_tmp); + RemoveFile(source_tmp); + if (!header_restored || !source_restored) { + const bool cleared = RemoveOutputPair(header_path, source_path); + *error = cleared + ? "cannot publish or restore output files" + : "cannot publish output files; manual cleanup required"; + } else { + *error = "cannot publish output files"; + } + return false; + } + + RemoveFile(header_backup); + RemoveFile(source_backup); + return true; +} + +void Usage(std::ostream& out) { + out << "Usage: brpc_flatc [-I include_dir]... " + "[-o existing_output_dir] schema.fbs\n" + << "Run official flatc --cpp separately to produce " + "schema_generated.h.\n"; +} + +int Run(int argc, char** argv) { + std::string input; + std::string output_dir = "."; + std::vector<std::string> include_dirs; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--help" || arg == "-h") { + Usage(std::cout); + return 0; + } + if (arg == "-o" || arg == "-I") { + if (++i == argc) { + Usage(std::cerr); + return 1; + } + if (arg == "-o") { + output_dir = argv[i]; + } else { + include_dirs.push_back(argv[i]); + } + } else if (arg.compare(0, 2, "-I") == 0 && arg.size() > 2) { + include_dirs.push_back(arg.substr(2)); + } else if (arg.empty() || arg[0] == '-' || !input.empty()) { + Usage(std::cerr); + return 1; + } else { + input = arg; + } + } + if (input.size() < 5 || input.substr(input.size() - 4) != ".fbs" || + output_dir.empty()) { + Usage(std::cerr); + return 1; + } + const size_t slash = input.find_last_of("/\\"); + const std::string basename = input.substr( + slash == std::string::npos ? 0 : slash + 1); + const std::string stem = basename.substr(0, basename.size() - 4); + if (stem.empty() || stem.find_first_not_of( + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-") != + std::string::npos) { + std::cerr << "brpc_flatc: unsupported schema filename\n"; + return 1; + } + include_dirs.push_back(slash == std::string::npos ? "." : + input.substr(0, slash == 0 ? 1 : slash)); + std::vector<const char*> include_paths; + for (const auto& directory : include_dirs) { + include_paths.push_back(directory.c_str()); + } + include_paths.push_back(nullptr); + std::ifstream stream(input.c_str(), std::ios::binary); + if (!stream) { + std::cerr << "brpc_flatc: cannot read " << input << '\n'; + return 1; + } + const std::string schema((std::istreambuf_iterator<char>(stream)), + std::istreambuf_iterator<char>()); + if (stream.bad() || schema.find('\0') != std::string::npos) { + std::cerr << "brpc_flatc: invalid schema input\n"; + return 1; + } + flatbuffers::Parser parser; + if (!parser.Parse(schema.c_str(), include_paths.data(), input.c_str())) { + std::cerr << parser.error_ << '\n'; + return 1; + } + std::vector<Service> services; + std::string error; + if (!CollectServices(parser, &services, &error)) { + std::cerr << "brpc_flatc: " << error << '\n'; + return 1; + } + // Coexisting service headers cannot define the same qualified service. + // Preserve filename bytes and that service identity, without tying output + // to checkout paths or folding punctuation and case into the same guard. + const std::string guard = + "BRPC_FLATBUFFERS_GENERATED_" + GuardComponent(stem) + "_" + + GuardComponent(Qualified(*services.front().definition)) + "_H_"; + std::ostringstream header; + header << kLicense << "#ifndef " << guard << "\n#define " << guard << "\n\n" + << "#include <memory>\n" + << "#include <google/protobuf/service.h>\n" + << "#include \"brpc/flatbuffers/message.h\"\n" + << "#include \"brpc/flatbuffers/service.h\"\n" + << "#include \"" << stem << "_generated.h\"\n\n" + << "#if !BRPC_WITH_FLATBUFFERS\n" + << "#error \"Generated services require BRPC_WITH_FLATBUFFERS\"\n" + << "#endif\n\n"; + std::ostringstream source; + source << kLicense << "#include \"" << stem << ".brpc.fb.h\"\n\n" + << "#include <stdexcept>\n\n" + << "namespace {\n" + << "void BrpcFlatbuffersFail(" + "::google::protobuf::RpcController* controller,\n" + << " ::google::protobuf::Closure* done,\n" + << " const char* reason) {\n" Review Comment: Thanks! The shared helper is gone. Each generated method now handles the failure inline, so a table or service named `BrpcFlatbuffersFail` no longer conflicts. Covered by the acceptance tests in 60550a3d. ########## tools/flatbuffers/brpc_flatc.cpp: ########## @@ -0,0 +1,761 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <cerrno> +#include <cstdio> +#include <cstdint> +#include <cstdlib> +#include <exception> +#include <fstream> +#include <iostream> +#include <iterator> +#include <limits> +#include <set> +#include <sstream> +#include <string> +#include <vector> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> +#include <flatbuffers/idl.h> + +namespace { + +const char kLicense[] = R"(// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Generated by brpc_flatc. Do not edit. + +)"; + +const char kArguments[] = + "::google::protobuf::RpcController* controller,\n" + " const ::brpc::flatbuffers::Message* request,\n" + " ::brpc::flatbuffers::Message* response,\n" + " ::google::protobuf::Closure* done"; + +struct Service { + const flatbuffers::ServiceDef* definition; + std::vector<int32_t> ids; +}; + +std::string JoinNamespace(const flatbuffers::Definition& definition, + const std::string& separator) { + std::string result; + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + if (!result.empty()) { + result += separator; + } + result += component; + } + } + return result; +} + +std::string Qualified(const flatbuffers::Definition& definition) { + const std::string ns = JoinNamespace(definition, "::"); + return "::" + (ns.empty() ? "" : ns + "::") + definition.name; +} + +std::string GuardComponent(const std::string& value) { + static const char digits[] = "0123456789ABCDEF"; + std::string result; + for (unsigned char c : value) { + result += digits[c >> 4]; + result += digits[c & 15]; + } + return result; +} + +bool IsCppIdentifier(const std::string& name) { + static const std::set<std::string> keywords = { + "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", + "bitor", "bool", "break", "case", "catch", "char", "char8_t", + "char16_t", "char32_t", "class", "compl", "concept", "const", + "const_cast", "consteval", "constexpr", "constinit", "continue", + "co_await", "co_return", "co_yield", "decltype", "default", + "delete", "do", + "double", "dynamic_cast", "else", "enum", "explicit", "export", + "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", + "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", + "private", "protected", "public", "register", "reinterpret_cast", + "requires", "return", "short", "signed", "sizeof", "static", + "static_assert", "static_cast", "struct", "switch", "template", + "this", "thread_local", "throw", "true", "try", "typedef", + "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + return !name.empty() && keywords.count(name) == 0; Review Comment: Agreed, thanks. Names reserved in C++ are now rejected: anything containing `__`, `_` followed by an uppercase letter, and a leading `_` at global scope. The generated `_Stub` name is checked as well. Tests added in 60550a3d. ########## tools/flatbuffers/brpc_flatc.cpp: ########## @@ -0,0 +1,890 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <cerrno> +#include <cstdio> +#include <cstdint> +#include <cstdlib> +#include <exception> +#include <fstream> +#include <iostream> +#include <iterator> +#include <limits> +#include <set> +#include <sstream> +#include <string> +#include <vector> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> +#include <flatbuffers/idl.h> + +#if FLATBUFFERS_VERSION_MAJOR != 25 || FLATBUFFERS_VERSION_MINOR != 2 || \ + FLATBUFFERS_VERSION_REVISION != 10 +#error "brpc_flatc requires FlatBuffers 25.2.10 headers and library" +#endif + +namespace { + +const char kLicense[] = R"(// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Generated by brpc_flatc. Do not edit. + +)"; + +const char kArguments[] = + "::google::protobuf::RpcController* controller,\n" + " const ::brpc::flatbuffers::Message* request,\n" + " ::brpc::flatbuffers::Message* response,\n" + " ::google::protobuf::Closure* done"; + +struct Service { + const flatbuffers::ServiceDef* definition; + std::vector<int32_t> ids; +}; + +std::string JoinNamespace(const flatbuffers::Definition& definition, + const std::string& separator) { + std::string result; + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + if (!result.empty()) { + result += separator; + } + result += component; + } + } + return result; +} + +std::string Qualified(const flatbuffers::Definition& definition) { + const std::string ns = JoinNamespace(definition, "::"); + return "::" + (ns.empty() ? "" : ns + "::") + definition.name; +} + +std::string GuardComponent(const std::string& value) { + static const char digits[] = "0123456789ABCDEF"; + std::string result; + for (unsigned char c : value) { + result += digits[c >> 4]; + result += digits[c & 15]; + } + return result; +} + +bool IsCppIdentifier(const std::string& name) { + static const std::set<std::string> keywords = { + "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", + "bitor", "bool", "break", "case", "catch", "char", "char8_t", + "char16_t", "char32_t", "class", "compl", "concept", "const", + "const_cast", "consteval", "constexpr", "constinit", "continue", + "co_await", "co_return", "co_yield", "decltype", "default", + "delete", "do", + "double", "dynamic_cast", "else", "enum", "explicit", "export", + "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", + "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", + "private", "protected", "public", "register", "reinterpret_cast", + "requires", "return", "short", "signed", "sizeof", "static", + "static_assert", "static_cast", "struct", "switch", "template", + "this", "thread_local", "throw", "true", "try", "typedef", + "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + return !name.empty() && keywords.count(name) == 0; +} + +std::string FlatcName(const std::string& name) { + // flatc 25.2.10 escapes a different set than the C++20 rejection list. + static const std::set<std::string> extra = { + "atomic_cancel", "atomic_commit", "atomic_noexcept", "import", + "module", "synchronized" + }; + const bool newer_keyword = name == "char8_t" || name == "consteval" || + name == "constinit"; + return ((!IsCppIdentifier(name) && !newer_keyword) || extra.count(name)) + ? name + "_" : name; +} + +std::string FlatcNamespace(const flatbuffers::Definition& definition) { + const std::string ns = JoinNamespace(definition, "::"); + return ns.empty() ? "::" : "::" + ns + "::"; +} + +bool IsReservedIdentifier(const std::string& name, bool global) { + return name.find("__") != std::string::npos || + (!name.empty() && name[0] == '_' && + (global || (name.size() > 1 && name[1] >= 'A' && name[1] <= 'Z'))); +} + +bool ValidateName(const flatbuffers::Definition& definition, + std::string* error, bool member = false) { + if (!IsCppIdentifier(definition.name)) { + *error = "C++ keyword is not supported: " + definition.name; + return false; + } + const bool global = !member && (!definition.defined_namespace || + definition.defined_namespace->components.empty()); + if (IsReservedIdentifier(definition.name, global)) { + *error = "reserved C++ identifier: " + definition.name; + return false; + } + if (definition.defined_namespace) { + bool first = true; + for (const auto& component : definition.defined_namespace->components) { + if (IsReservedIdentifier(component, first)) { + *error = "reserved C++ identifier in namespace: " + component; + return false; + } + first = false; + if (!IsCppIdentifier(component)) { + *error = "C++ keyword namespace is not supported: " + component; + return false; + } + } + } + return true; +} + +bool ParseId(const flatbuffers::Value& value, int32_t* id) { + if (value.constant.empty() || + !flatbuffers::IsInteger(value.type.base_type)) { + return false; + } + int64_t number = 0; + for (char c : value.constant) { + if (c < '0' || c > '9') { + return false; + } + number = number * 10 + (c - '0'); + if (number > std::numeric_limits<int32_t>::max()) { + return false; + } + } + *id = static_cast<int32_t>(number); + return true; +} + +void CollectRootNames(const flatbuffers::Parser& parser, + std::set<std::string>* names) { + if (!parser.root_struct_def_) { + return; + } + const auto& root = *parser.root_struct_def_; + const std::string ns = FlatcNamespace(root); + const std::string name = FlatcName(root.name); + names->insert(ns + "Get" + name); + names->insert(ns + "GetSizePrefixed" + name); + names->insert(ns + "Verify" + name + "Buffer"); + names->insert(ns + "VerifySizePrefixed" + name + "Buffer"); + names->insert(ns + "Finish" + name + "Buffer"); + names->insert(ns + "FinishSizePrefixed" + name + "Buffer"); + if (!parser.file_identifier_.empty()) { + names->insert(ns + name + "Identifier"); + names->insert(ns + name + "BufferHasIdentifier"); + names->insert(ns + "SizePrefixed" + name + "BufferHasIdentifier"); + } + if (!parser.file_extension_.empty()) { + names->insert(ns + name + "Extension"); + } +} + +bool CollectServices(const flatbuffers::Parser& parser, + const std::string& input, const char** include_paths, + std::vector<Service>* services, std::string* error) { + std::set<std::string> generated_class_names = { + "::brpc", "::butil", "::flatbuffers", "::google", "::std" + }; + CollectRootNames(parser, &generated_class_names); + // The parser resets root_type after each include; flatc emits those helpers + // when each imported schema is generated separately. + for (const auto& file : parser.GetIncludedFilesRecursive(input)) { + if (file == input) { + continue; + } + std::string schema; + flatbuffers::Parser included; + if (!flatbuffers::LoadFile(file.c_str(), false, &schema) || + !included.Parse(schema.c_str(), include_paths, file.c_str())) { + *error = "cannot inspect imported root_type: " + file; + return false; + } + CollectRootNames(included, &generated_class_names); + } + // Reserve every namespace prefix, including those from imported schemas. + for (const auto* ns : parser.namespaces_) { + std::string prefix; + for (const auto& component : ns->components) { + prefix += "::" + component; + generated_class_names.insert(prefix); + } + } + for (const auto* definition : parser.services_.vec) { + if (definition->generated) { + const std::string service_name = Qualified(*definition); + generated_class_names.insert(service_name); + generated_class_names.insert(service_name + "_Stub"); + } + } + for (const auto* definition : parser.structs_.vec) { + const std::string ns = FlatcNamespace(*definition); + const std::string name = FlatcName(definition->name); + const std::string type_name = ns + name; + generated_class_names.insert(type_name); + if (!definition->fixed) { + generated_class_names.insert(type_name + "Builder"); + generated_class_names.insert(ns + "Create" + name); + for (const auto* field : definition->fields.vec) { + if (!field->deprecated && + (flatbuffers::IsString(field->value.type) || + flatbuffers::IsVector(field->value.type))) { + generated_class_names.insert( + ns + "Create" + name + "Direct"); + break; + } + } + } + } + // Match the default flatc --cpp API, including non-type declarations. + for (const auto* definition : parser.enums_.vec) { + const std::string ns = FlatcNamespace(*definition); + const std::string name = FlatcName(definition->name); + const std::string enum_name = ns + name; + generated_class_names.insert(enum_name); + for (const auto* value : definition->Vals()) { + generated_class_names.insert(enum_name + "_" + FlatcName(value->name)); + } + if (definition->attributes.Lookup("bit_flags")) { + generated_class_names.insert(enum_name + "_NONE"); + generated_class_names.insert(enum_name + "_ANY"); + } else { + generated_class_names.insert(enum_name + "_MIN"); + generated_class_names.insert(enum_name + "_MAX"); + } + generated_class_names.insert(ns + "EnumValues" + name); + generated_class_names.insert(ns + "EnumName" + name); + if (definition->Distance() / definition->size() < 5) { + generated_class_names.insert(ns + "EnumNames" + name); + } + if (definition->is_union) { + if (!definition->uses_multiple_type_instances) { + generated_class_names.insert(enum_name + "Traits"); + } + generated_class_names.insert(ns + "Verify" + name); + generated_class_names.insert( + ns + "Verify" + name + "Vector"); + } + } + for (const auto* definition : parser.services_.vec) { + // Included schemas are generated separately, just as with flatc --cpp. + if (definition->generated) { + continue; + } + if (!ValidateName(*definition, error)) { + return false; + } + if (definition->name == "Stub" || definition->name == "descriptor" || + definition->name == "GetDescriptor" || + definition->name == "FBCallMethod") { + *error = "service name collides with generated API: " + + definition->name; + return false; + } + const std::string service_name = Qualified(*definition); + const std::string stub_name = service_name + "_Stub"; + if (IsReservedIdentifier(definition->name + "_Stub", false)) { + *error = "generated stub uses reserved C++ identifier: " + stub_name; + return false; + } + if (!generated_class_names.insert(service_name).second) { + *error = "generated service class name collides: " + service_name; + return false; + } + if (!generated_class_names.insert(stub_name).second) { + *error = "generated service class name collides: " + stub_name; + return false; + } + Service service = {definition, {}}; + std::set<int32_t> ids; + if (definition->calls.vec.empty()) { + *error = "rpc_service must contain at least one method: " + + definition->name; + return false; + } + for (const auto* call : definition->calls.vec) { + if (!ValidateName(*call, error, true) || + !ValidateName(*call->request, error) || + !ValidateName(*call->response, error)) { + return false; + } + if (call->name == definition->name || + call->name == definition->name + "_Stub" || + call->name == "descriptor" || call->name == "GetDescriptor" || + call->name == "FBCallMethod" || call->name == "Stub" || + call->name == "channel" || call->name == "channel_" || + call->name == "owned_channel_") { + *error = "method name collides with generated API: " + + call->name; + return false; + } + const auto* attribute = call->attributes.Lookup("id"); + int32_t id = 0; + if (!attribute || !ParseId(*attribute, &id)) { + *error = definition->name + "." + call->name + + " requires an explicit nonnegative int32 (id: N)"; + return false; + } + if (!ids.insert(id).second) { + *error = "duplicate method id " + std::to_string(id) + + " in " + definition->name; + return false; + } + if (call->attributes.Lookup("streaming")) { + *error = "streaming RPC is not supported: " + call->name; + return false; + } + service.ids.push_back(id); + } + services->push_back(service); + } + if (services->empty()) { + *error = "input schema contains no rpc_service to generate"; + return false; + } + return true; +} + +void OpenNamespace(const flatbuffers::Definition& definition, + std::ostream& out) { + if (definition.defined_namespace) { + for (const auto& component : definition.defined_namespace->components) { + out << "namespace " << component << " {\n"; + } + } + out << '\n'; +} + +void CloseNamespace(const flatbuffers::Definition& definition, + std::ostream& out) { + if (definition.defined_namespace) { + const auto& components = definition.defined_namespace->components; + for (auto it = components.rbegin(); it != components.rend(); ++it) { + out << "} // namespace " << *it << '\n'; + } + } + out << '\n'; +} + +void GenerateHeader(const Service& service, std::ostream& out) { + const auto& definition = *service.definition; + const std::string& name = definition.name; + OpenNamespace(definition, out); + out << "class " << name << "_Stub;\n\n" + << "class " << name << " : public ::brpc::flatbuffers::Service {\n" + << "public:\n" + << " typedef " << name << "_Stub Stub;\n" + << " static const ::brpc::flatbuffers::ServiceDescriptor* " + "descriptor();\n" + << " const ::brpc::flatbuffers::ServiceDescriptor* " + "GetDescriptor() override;\n" + << " void FBCallMethod(\n" + << " const ::brpc::flatbuffers::MethodDescriptor* method,\n" + << " " << kArguments << ") override;\n"; + for (const auto* call : definition.calls.vec) { + out << " virtual void " << call->name << "(\n" + << " " << kArguments << ");\n"; + } + out << "};\n\n" + << "class " << name << "_Stub : public " << name << " {\n" + << "public:\n" + << " explicit " << name << "_Stub(\n" + << " ::brpc::flatbuffers::RpcChannel* channel,\n" + << " ::brpc::flatbuffers::Service::ChannelOwnership " + "ownership =\n" + << " ::brpc::flatbuffers::Service::" + "STUB_DOESNT_OWN_CHANNEL);\n" + << " ::brpc::flatbuffers::RpcChannel* channel() const {\n" + << " return channel_;\n" + << " }\n"; + for (const auto* call : definition.calls.vec) { + out << " void " << call->name << "(\n" + << " " << kArguments << ") override;\n"; + } + out << "\nprivate:\n" + << " ::brpc::flatbuffers::RpcChannel* channel_;\n" + << " ::std::unique_ptr<::brpc::flatbuffers::RpcChannel> " + "owned_channel_;\n" + << "};\n\n"; + CloseNamespace(definition, out); +} + +void GenerateFailureHandler(std::ostream& out) { + // A local callable cannot be hidden by schema declarations or RPC names. + out << " const auto fail = [controller, done](const char* reason) {\n" + << " if (controller) {\n" + << " controller->SetFailed(reason);\n" + << " }\n" + << " if (done) {\n" + << " done->Run();\n" + << " }\n" + << " };\n"; +} + +void GenerateSource(const Service& service, std::ostream& out) { + const auto& definition = *service.definition; + const std::string& name = definition.name; + OpenNamespace(definition, out); + out << "const ::brpc::flatbuffers::ServiceDescriptor* " << name + << "::descriptor() {\n" + << " struct Holder {\n" + << " ::brpc::flatbuffers::ServiceDescriptor value;\n" + << " Holder() {\n" + << " const ::brpc::flatbuffers::BrpcDescriptorTable " + "table = {\n" + << " \"" << JoinNamespace(definition, ".") << "\", \"" + << name << "\",\n \""; + for (size_t i = 0; i < definition.calls.vec.size(); ++i) { + out << (i == 0 ? "" : " ") << definition.calls.vec[i]->name; + } + out << "\", {"; + for (size_t i = 0; i < service.ids.size(); ++i) { + out << (i == 0 ? "" : ", ") << service.ids[i]; + } + out << "}\n };\n" + << " if (value.init(table) != 0) {\n" + << " throw ::std::runtime_error(\n" + << " \"invalid generated service descriptor\");\n" + << " }\n" + << " }\n" + << " };\n" + << " static const Holder holder;\n" + << " return &holder.value;\n" + << "}\n\n" + << "const ::brpc::flatbuffers::ServiceDescriptor* " << name + << "::GetDescriptor() {\n" + << " return descriptor();\n" + << "}\n\n" + << "void " << name << "::FBCallMethod(\n" + << " const ::brpc::flatbuffers::MethodDescriptor* method,\n" + << " " << kArguments << ") {\n"; + GenerateFailureHandler(out); + out << " if (!method || method->service() != descriptor() ||\n" + << " descriptor()->FindMethodByIndex(method->index()) != " + "method) {\n" + << " fail(\"invalid service method\");\n" + << " return;\n" + << " }\n" + << " if (!request || !response) {\n" + << " fail(\"null request or response\");\n" + << " return;\n" + << " }\n" + << " switch (method->index()) {\n"; + for (size_t i = 0; i < definition.calls.vec.size(); ++i) { + const auto& call = *definition.calls.vec[i]; + out << " case " << service.ids[i] << ":\n" + << " if (!request->Verify<" << Qualified(*call.request) + << ">()) {\n" Review Comment: Thanks, confirmed. `Verify<>` now uses the type name flatc actually emits (`::api::module_`), while the service identity keeps the schema name. Compile and runtime coverage for global, nested and imported types is in 60550a3d. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
