From 48f37f9e0a04f2b60046dda7fef20a8b0ebc1a70 Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Wed, 13 Apr 2016 18:16:05 -0700 Subject: [PATCH] Added GRPC code generator to flatc. Also added simple (in-process) test. Change-Id: I38580d554dd52f590e3396ec4846e07546dcf07d Tested: on Linux. --- CMakeLists.txt | 23 + docs/source/Compiler.md | 2 + docs/source/Schemas.md | 3 +- grpc/README.md | 11 + grpc/src/compiler/cpp_generator.cc | 1202 ++++++++++++++++++++++++++++ grpc/src/compiler/cpp_generator.h | 147 ++++ grpc/tests/grpctest.cpp | 124 +++ include/flatbuffers/flatbuffers.h | 23 + include/flatbuffers/grpc.h | 72 ++ include/flatbuffers/idl.h | 8 +- src/flatc.cpp | 6 +- src/idl_gen_grpc.cpp | 216 +++++ src/idl_parser.cpp | 4 + tests/generate_code.bat | 2 +- tests/generate_code.sh | 2 +- tests/monster_test.fbs | 2 +- tests/monster_test.grpc.fb.cc | 85 ++ tests/monster_test.grpc.fb.h | 155 ++++ 18 files changed, 2081 insertions(+), 6 deletions(-) create mode 100644 grpc/README.md create mode 100644 grpc/src/compiler/cpp_generator.cc create mode 100644 grpc/src/compiler/cpp_generator.h create mode 100644 grpc/tests/grpctest.cpp create mode 100644 include/flatbuffers/grpc.h create mode 100644 src/idl_gen_grpc.cpp create mode 100644 tests/monster_test.grpc.fb.cc create mode 100644 tests/monster_test.grpc.fb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b1e56d6b0..c2dd95085 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ option(FLATBUFFERS_INSTALL "Enable the installation of targets." ON) option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library" ON) option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler" ON) option(FLATBUFFERS_BUILD_FLATHASH "Enable the build of flathash" ON) +option(FLATBUFFERS_BUILD_GRPCTEST "Enable the build of grpctest" OFF) if(NOT FLATBUFFERS_BUILD_FLATC AND FLATBUFFERS_BUILD_TESTS) message(WARNING @@ -39,7 +40,10 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_php.cpp src/idl_gen_python.cpp src/idl_gen_fbs.cpp + src/idl_gen_grpc.cpp src/flatc.cpp + grpc/src/compiler/cpp_generator.h + grpc/src/compiler/cpp_generator.cc ) set(FlatHash_SRCS @@ -76,6 +80,16 @@ set(FlatBuffers_Sample_Text_SRCS ${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h ) +set(FlatBuffers_GRPCTest_SRCS + include/flatbuffers/flatbuffers.h + include/flatbuffers/grpc.h + tests/monster_test.grpc.fb.h + tests/monster_test.grpc.fb.cc + grpc/tests/grpctest.cpp + # file generated by running compiler on samples/monster.fbs + ${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h +) + # source_group(Compiler FILES ${FlatBuffers_Compiler_SRCS}) # source_group(Tests FILES ${FlatBuffers_Tests_SRCS}) @@ -129,6 +143,7 @@ if(BIICODE) endif() include_directories(include) +include_directories(grpc) if(FLATBUFFERS_BUILD_FLATLIB) add_library(flatbuffers STATIC ${FlatBuffers_Library_SRCS}) @@ -174,6 +189,14 @@ if(FLATBUFFERS_BUILD_TESTS) add_executable(flatsampletext ${FlatBuffers_Sample_Text_SRCS}) endif() +if(FLATBUFFERS_BUILD_GRPCTEST) + if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") + endif() + add_executable(grpctest ${FlatBuffers_GRPCTest_SRCS}) + target_link_libraries(grpctest grpc++_unsecure grpc pthread dl) +endif() + if(FLATBUFFERS_INSTALL) install(DIRECTORY include/flatbuffers DESTINATION include) if(FLATBUFFERS_BUILD_FLATLIB) diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 886fbd9b8..ad584c739 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -33,6 +33,8 @@ For any schema input files, one or more generators can be specified: - `--php`: Generate PHP code. +- `--grpc`: Generate RPC stub code for GRPC. + For any data input files: - `--binary`, `-b` : If data is contained in this file, generate a diff --git a/docs/source/Schemas.md b/docs/source/Schemas.md index 14bd3bd0e..7dcce0711 100755 --- a/docs/source/Schemas.md +++ b/docs/source/Schemas.md @@ -237,7 +237,8 @@ as the response (both of which must be table types): } What code this produces and how it is used depends on language and RPC system -used, FlatBuffers itself does not offer this functionality. +used, there is preliminary support for GRPC through the `--grpc` code generator, +see `grpc/tests` for an example. ### Comments & documentation diff --git a/grpc/README.md b/grpc/README.md new file mode 100644 index 000000000..13485198c --- /dev/null +++ b/grpc/README.md @@ -0,0 +1,11 @@ +GRPC implementation and test +============================ + +NOTE: files in `src/` are shared with the GRPC project, and maintained there +(any changes should be submitted to GRPC instead). These files are copied +from GRPC, and work with both the Protobuf and FlatBuffers code generator. + +`tests/` contains a GRPC specific test, you need to have built and installed +the GRPC libraries for this to compile. This test will build using the +`FLATBUFFERS_BUILD_GRPCTEST` option to the main FlatBuffers CMake project. + diff --git a/grpc/src/compiler/cpp_generator.cc b/grpc/src/compiler/cpp_generator.cc new file mode 100644 index 000000000..9319c4193 --- /dev/null +++ b/grpc/src/compiler/cpp_generator.cc @@ -0,0 +1,1202 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include "src/compiler/cpp_generator.h" + +#include + +namespace grpc_cpp_generator { +namespace { + +template +grpc::string as_string(T x) { + std::ostringstream out; + out << x; + return out.str(); +} + +grpc::string FilenameIdentifier(const grpc::string &filename) { + grpc::string result; + for (unsigned i = 0; i < filename.size(); i++) { + char c = filename[i]; + if (isalnum(c)) { + result.push_back(c); + } else { + static char hex[] = "0123456789abcdef"; + result.push_back('_'); + result.push_back(hex[(c >> 4) & 0xf]); + result.push_back(hex[c & 0xf]); + } + } + return result; +} +} // namespace + +template +T *array_end(T (&array)[N]) { return array + N; } + +void PrintIncludes(Printer *printer, const std::vector& headers, const Parameters ¶ms) { + std::map vars; + + vars["l"] = params.use_system_headers ? '<' : '"'; + vars["r"] = params.use_system_headers ? '>' : '"'; + + if (!params.grpc_search_path.empty()) { + vars["l"] += params.grpc_search_path; + if (params.grpc_search_path.back() != '/') { + vars["l"] += '/'; + } + } + + for (auto i = headers.begin(); i != headers.end(); i++) { + vars["h"] = *i; + printer->Print(vars, "#include $l$$h$$r$\n"); + } +} + +grpc::string GetHeaderPrologue(File *file, const Parameters & /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + vars["filename"] = file->filename(); + vars["filename_identifier"] = FilenameIdentifier(file->filename()); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = file->message_header_ext(); + + printer->Print(vars, "// Generated by the gRPC protobuf plugin.\n"); + printer->Print(vars, + "// If you make any local change, they will be lost.\n"); + printer->Print(vars, "// source: $filename$\n"); + printer->Print(vars, "#ifndef GRPC_$filename_identifier$__INCLUDED\n"); + printer->Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n"); + printer->Print(vars, "\n"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, "\n"); + } + return output; +} + +grpc::string GetHeaderIncludes(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + static const char *headers_strs[] = { + "grpc++/impl/codegen/async_stream.h", + "grpc++/impl/codegen/async_unary_call.h", + "grpc++/impl/codegen/proto_utils.h", + "grpc++/impl/codegen/rpc_method.h", + "grpc++/impl/codegen/service_type.h", + "grpc++/impl/codegen/status.h", + "grpc++/impl/codegen/stub_options.h", + "grpc++/impl/codegen/sync_stream.h" + }; + std::vector headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params); + printer->Print(vars, "\n"); + printer->Print(vars, "namespace grpc {\n"); + printer->Print(vars, "class CompletionQueue;\n"); + printer->Print(vars, "class Channel;\n"); + printer->Print(vars, "class RpcService;\n"); + printer->Print(vars, "class ServerCompletionQueue;\n"); + printer->Print(vars, "class ServerContext;\n"); + printer->Print(vars, "} // namespace grpc\n\n"); + + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer->Print(vars, "namespace $part$ {\n"); + } + printer->Print(vars, "\n"); + } + } + return output; +} + +void PrintHeaderClientMethodInterfaces( + Printer *printer, const Method *method, + std::map *vars, bool is_public) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + if (is_public) { + if (method->NoStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::Status $Method$(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) = 0;\n"); + printer->Print(*vars, + "std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>>(" + "Async$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>" + " $Method$(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>" + "($Method$Raw(context, response));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncWriterInterface< $Request$>>" + " Async$Method$(::grpc::ClientContext* context, $Response$* " + "response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncWriterInterface< $Request$>>(" + "Async$Method$Raw(context, response, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>" + " $Method$(::grpc::ClientContext* context, const $Request$& request)" + " {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>" + "($Method$Raw(context, request));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderInterface< $Response$>> " + "Async$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderInterface< $Response$>>(" + "Async$Method$Raw(context, request, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientReaderWriterInterface< " + "$Request$, $Response$>> " + "$Method$(::grpc::ClientContext* context) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientReaderWriterInterface< $Request$, $Response$>>(" + "$Method$Raw(context));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>>(" + "Async$Method$Raw(context, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else { + if (method->NoStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncResponseReaderInterface< $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) = 0;\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::ClientWriterInterface< $Request$>*" + " $Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) = 0;\n"); + printer->Print(*vars, + "virtual ::grpc::ClientAsyncWriterInterface< $Request$>*" + " Async$Method$Raw(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::ClientReaderInterface< $Response$>* $Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request) = 0;\n"); + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncReaderInterface< $Response$>* " + "Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "virtual ::grpc::ClientReaderWriterInterface< $Request$, " + "$Response$>* " + "$Method$Raw(::grpc::ClientContext* context) = 0;\n"); + printer->Print(*vars, + "virtual ::grpc::ClientAsyncReaderWriterInterface< " + "$Request$, $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) = 0;\n"); + } + } +} + +void PrintHeaderClientMethod(Printer *printer, + const Method *method, + std::map *vars, + bool is_public) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (is_public) { + if (method->NoStreaming()) { + printer->Print( + *vars, + "::grpc::Status $Method$(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncResponseReader< $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReader< $Response$>>(" + "Async$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientWriter< $Request$>>" + " $Method$(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< ::grpc::ClientWriter< $Request$>>" + "($Method$Raw(context, response));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>" + " Async$Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>(" + "Async$Method$Raw(context, response, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReader< $Response$>>" + " $Method$(::grpc::ClientContext* context, const $Request$& request)" + " {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientReader< $Response$>>" + "($Method$Raw(context, request));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> " + "Async$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>>(" + "Async$Method$Raw(context, request, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientReaderWriter< $Request$, $Response$>>" + " $Method$(::grpc::ClientContext* context) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientReaderWriter< $Request$, $Response$>>(" + "$Method$Raw(context));\n"); + printer->Outdent(); + printer->Print("}\n"); + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderWriter< " + "$Request$, $Response$>> " + "Async$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>>(" + "Async$Method$Raw(context, cq, tag));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else { + if (method->NoStreaming()) { + printer->Print(*vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) GRPC_OVERRIDE;\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "::grpc::ClientWriter< $Request$>* $Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) " + "GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncWriter< $Request$>* Async$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print(*vars, + "::grpc::ClientReader< $Response$>* $Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request)" + " GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReader< $Response$>* Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$Method$Raw(::grpc::ClientContext* context) GRPC_OVERRIDE;\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;\n"); + } + } +} + +void PrintHeaderClientMethodData(Printer *printer, const Method *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n"); +} + +void PrintHeaderServerMethodSync(Printer *printer, const Method *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response);\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response);\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print(*vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer);\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "virtual ::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream);" + "\n"); + } +} + +void PrintHeaderServerMethodAsync( + Printer *printer, + const Method *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + printer->Print(*vars, "template \n"); + printer->Print(*vars, + "class WithAsyncMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithAsyncMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodAsync($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithAsyncMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + if (method->NoStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, $Request$* request, " + "::grpc::ServerAsyncResponseWriter< $Response$>* response, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncUnary($Idx$, context, " + "request, response, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerAsyncReader< $Response$, $Request$>* reader, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncClientStreaming($Idx$, " + "context, reader, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, $Request$* request, " + "::grpc::ServerAsyncWriter< $Response$>* writer, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print( + *vars, + " ::grpc::Service::RequestAsyncServerStreaming($Idx$, " + "context, request, writer, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + "GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "void Request$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, " + "::grpc::CompletionQueue* new_call_cq, " + "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); + printer->Print(*vars, + " ::grpc::Service::RequestAsyncBidiStreaming($Idx$, " + "context, stream, new_call_cq, notification_cq, tag);\n"); + printer->Print("}\n"); + } + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderServerMethodGeneric( + Printer *printer, + const Method *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + printer->Print(*vars, "template \n"); + printer->Print(*vars, + "class WithGenericMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithGenericMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodGeneric($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithGenericMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + if (method->NoStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + "GRPC_FINAL GRPC_OVERRIDE {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + } + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderService(Printer *printer, + const Service *service, + std::map *vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "class $Service$ GRPC_FINAL {\n" + " public:\n"); + printer->Indent(); + + // Client side + printer->Print( + "class StubInterface {\n" + " public:\n"); + printer->Indent(); + printer->Print("virtual ~StubInterface() {}\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars, true); + } + printer->Outdent(); + printer->Print("private:\n"); + printer->Indent(); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars, false); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print( + "class Stub GRPC_FINAL : public StubInterface" + " {\n public:\n"); + printer->Indent(); + printer->Print("Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i).get(), vars, true); + } + printer->Outdent(); + printer->Print("\n private:\n"); + printer->Indent(); + printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i).get(), vars, false); + } + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethodData(printer, service->method(i).get(), vars); + } + printer->Outdent(); + printer->Print("};\n"); + printer->Print( + "static std::unique_ptr NewStub(const std::shared_ptr< " + "::grpc::ChannelInterface>& channel, " + "const ::grpc::StubOptions& options = ::grpc::StubOptions());\n"); + + printer->Print("\n"); + + // Server side - base + printer->Print( + "class Service : public ::grpc::Service {\n" + " public:\n"); + printer->Indent(); + printer->Print("Service();\n"); + printer->Print("virtual ~Service();\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderServerMethodSync(printer, service->method(i).get(), vars); + } + printer->Outdent(); + printer->Print("};\n"); + + // Server side - Asynchronous + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodAsync(printer, service->method(i).get(), vars); + } + + printer->Print("typedef "); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i).get()->name(); + printer->Print(*vars, "WithAsyncMethod_$method_name$<"); + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + printer->Print(" >"); + } + printer->Print(" AsyncService;\n"); + + // Server side - Generic + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodGeneric(printer, service->method(i).get(), vars); + } + + printer->Outdent(); + printer->Print("};\n"); +} + +grpc::string GetHeaderServices(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + // Package string is empty or ends with a dot. It is used to fully qualify + // method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } + + if (!params.services_namespace.empty()) { + vars["services_namespace"] = params.services_namespace; + printer->Print(vars, "\nnamespace $services_namespace$ {\n\n"); + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintHeaderService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); + } + + if (!params.services_namespace.empty()) { + printer->Print(vars, "} // namespace $services_namespace$\n\n"); + } + } + return output; +} + +grpc::string GetHeaderEpilogue(File *file, const Parameters & /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + vars["filename"] = file->filename(); + vars["filename_identifier"] = FilenameIdentifier(file->filename()); + + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + for (auto part = parts.rbegin(); part != parts.rend(); part++) { + vars["part"] = *part; + printer->Print(vars, "} // namespace $part$\n"); + } + printer->Print(vars, "\n"); + } + + printer->Print(vars, "\n"); + printer->Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); + } + return output; +} + +grpc::string GetSourcePrologue(File *file, const Parameters & /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + vars["filename"] = file->filename(); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = file->message_header_ext(); + vars["service_header_ext"] = file->service_header_ext(); + + printer->Print(vars, "// Generated by the gRPC protobuf plugin.\n"); + printer->Print(vars, + "// If you make any local change, they will be lost.\n"); + printer->Print(vars, "// source: $filename$\n\n"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n"); + printer->Print(vars, file->additional_headers().c_str()); + printer->Print(vars, "\n"); + } + return output; +} + +grpc::string GetSourceIncludes(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + static const char *headers_strs[] = { + "grpc++/impl/codegen/async_stream.h", + "grpc++/impl/codegen/async_unary_call.h", + "grpc++/impl/codegen/channel_interface.h", + "grpc++/impl/codegen/client_unary_call.h", + "grpc++/impl/codegen/method_handler_impl.h", + "grpc++/impl/codegen/rpc_service_method.h", + "grpc++/impl/codegen/service_type.h", + "grpc++/impl/codegen/sync_stream.h" + }; + std::vector headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params); + + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer->Print(vars, "namespace $part$ {\n"); + } + } + + printer->Print(vars, "\n"); + } + return output; +} + +void PrintSourceClientMethod(Printer *printer, + const Method *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Stub::$Method$(" + "::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response) {\n"); + printer->Print(*vars, + " return ::grpc::BlockingUnaryCall(channel_.get(), " + "rpcmethod_$Method$_, " + "context, request, response);\n" + "}\n\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Print(*vars, + " return new " + "::grpc::ClientAsyncResponseReader< $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "::grpc::ClientWriter< $Request$>* " + "$ns$$Service$::Stub::$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientWriter< $Request$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, response);\n" + "}\n\n"); + printer->Print(*vars, + "::grpc::ClientAsyncWriter< $Request$>* " + "$ns$$Service$::Stub::Async$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientAsyncWriter< $Request$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, response, tag);\n" + "}\n\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "::grpc::ClientReader< $Response$>* " + "$ns$$Service$::Stub::$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientReader< $Response$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + printer->Print(*vars, + "::grpc::ClientAsyncReader< $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientAsyncReader< $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request, tag);\n" + "}\n\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::$Method$Raw(::grpc::ClientContext* context) {\n"); + printer->Print(*vars, + " return new ::grpc::ClientReaderWriter< " + "$Request$, $Response$>(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context);\n" + "}\n\n"); + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq, void* tag) {\n"); + printer->Print(*vars, + " return new " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, tag);\n" + "}\n\n"); + } +} + +void PrintSourceServerMethod(Printer *printer, + const Method *method, + std::map *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "const $Request$* request, $Response$* response) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) request;\n"); + printer->Print(" (void) response;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) reader;\n"); + printer->Print(" (void) response;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) request;\n"); + printer->Print(" (void) writer;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "::grpc::Status $ns$$Service$::Service::$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* " + "stream) {\n"); + printer->Print(" (void) context;\n"); + printer->Print(" (void) stream;\n"); + printer->Print( + " return ::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); + printer->Print("}\n\n"); + } +} + +void PrintSourceService(Printer *printer, + const Service *service, + std::map *vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "static const char* $prefix$$Service$_method_names[] = {\n"); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Method"] = service->method(i).get()->name(); + printer->Print(*vars, " \"/$Package$$Service$/$Method$\",\n"); + } + printer->Print(*vars, "};\n\n"); + + printer->Print(*vars, + "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub(" + "const std::shared_ptr< ::grpc::ChannelInterface>& channel, " + "const ::grpc::StubOptions& options) {\n" + " std::unique_ptr< $ns$$Service$::Stub> stub(new " + "$ns$$Service$::Stub(channel));\n" + " return stub;\n" + "}\n\n"); + printer->Print(*vars, + "$ns$$Service$::Stub::Stub(const std::shared_ptr< " + "::grpc::ChannelInterface>& channel)\n"); + printer->Indent(); + printer->Print(": channel_(channel)"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + (*vars)["Method"] = method->name(); + (*vars)["Idx"] = as_string(i); + if (method->NoStreaming()) { + (*vars)["StreamingType"] = "NORMAL_RPC"; + } else if (method->ClientOnlyStreaming()) { + (*vars)["StreamingType"] = "CLIENT_STREAMING"; + } else if (method->ServerOnlyStreaming()) { + (*vars)["StreamingType"] = "SERVER_STREAMING"; + } else { + (*vars)["StreamingType"] = "BIDI_STREAMING"; + } + printer->Print(*vars, + ", rpcmethod_$Method$_(" + "$prefix$$Service$_method_names[$Idx$], " + "::grpc::RpcMethod::$StreamingType$, " + "channel" + ")\n"); + } + printer->Print("{}\n\n"); + printer->Outdent(); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintSourceClientMethod(printer, service->method(i).get(), vars); + } + + printer->Print(*vars, "$ns$$Service$::Service::Service() {\n"); + printer->Indent(); + printer->Print(*vars, "(void)$prefix$$Service$_method_names;\n"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + (*vars)["Idx"] = as_string(i); + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::NORMAL_RPC,\n" + " new ::grpc::RpcMethodHandler< $ns$$Service$::Service, " + "$Request$, " + "$Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (method->ClientOnlyStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::CLIENT_STREAMING,\n" + " new ::grpc::ClientStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (method->ServerOnlyStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::SERVER_STREAMING,\n" + " new ::grpc::ServerStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "AddMethod(new ::grpc::RpcServiceMethod(\n" + " $prefix$$Service$_method_names[$Idx$],\n" + " ::grpc::RpcMethod::BIDI_STREAMING,\n" + " new ::grpc::BidiStreamingHandler< " + "$ns$$Service$::Service, $Request$, $Response$>(\n" + " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); + } + } + printer->Outdent(); + printer->Print(*vars, "}\n\n"); + printer->Print(*vars, + "$ns$$Service$::Service::~Service() {\n" + "}\n\n"); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintSourceServerMethod(printer, service->method(i).get(), vars); + } +} + +grpc::string GetSourceServices(File *file, + const Parameters ¶ms) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + // Package string is empty or ends with a dot. It is used to fully qualify + // method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } + if (!params.services_namespace.empty()) { + vars["ns"] = params.services_namespace + "::"; + vars["prefix"] = params.services_namespace; + } else { + vars["ns"] = ""; + vars["prefix"] = ""; + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintSourceService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); + } + } + return output; +} + +grpc::string GetSourceEpilogue(File *file, const Parameters & /*params*/) { + grpc::string temp; + + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + temp.append("} // namespace "); + temp.append(*part); + temp.append("\n"); + } + temp.append("\n"); + } + + return temp; +} + +} // namespace grpc_cpp_generator diff --git a/grpc/src/compiler/cpp_generator.h b/grpc/src/compiler/cpp_generator.h new file mode 100644 index 000000000..953ddfd56 --- /dev/null +++ b/grpc/src/compiler/cpp_generator.h @@ -0,0 +1,147 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H + +// cpp_generator.h/.cc do not directly depend on GRPC/ProtoBuf, such that they +// can be used to generate code for other serialization systems, such as +// FlatBuffers. + +#include +#include + +#ifndef GRPC_CUSTOM_STRING +#include +#define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +namespace grpc_cpp_generator { + +// Contains all the parameters that are parsed from the command line. +struct Parameters { + // Puts the service into a namespace + grpc::string services_namespace; + // Use system includes (<>) or local includes ("") + bool use_system_headers; + // Prefix to any grpc include + grpc::string grpc_search_path; +}; + +// An abstract interface representing a method. +struct Method { + virtual ~Method() {} + + virtual grpc::string name() const = 0; + + virtual grpc::string input_type_name() const = 0; + virtual grpc::string output_type_name() const = 0; + + virtual bool NoStreaming() const = 0; + virtual bool ClientOnlyStreaming() const = 0; + virtual bool ServerOnlyStreaming() const = 0; + virtual bool BidiStreaming() const = 0; +}; + +// An abstract interface representing a service. +struct Service { + virtual ~Service() {} + + virtual grpc::string name() const = 0; + + virtual int method_count() const = 0; + virtual std::unique_ptr method(int i) const = 0; +}; + +struct Printer { + virtual ~Printer() {} + + virtual void Print(const std::map &vars, + const char *template_string) = 0; + virtual void Print(const char *string) = 0; + virtual void Indent() = 0; + virtual void Outdent() = 0; +}; + +// An interface that allows the source generated to be output using various +// libraries/idls/serializers. +struct File { + virtual ~File() {} + + virtual grpc::string filename() const = 0; + virtual grpc::string filename_without_ext() const = 0; + virtual grpc::string message_header_ext() const = 0; + virtual grpc::string service_header_ext() const = 0; + virtual grpc::string package() const = 0; + virtual std::vector package_parts() const = 0; + virtual grpc::string additional_headers() const = 0; + + virtual int service_count() const = 0; + virtual std::unique_ptr service(int i) const = 0; + + virtual std::unique_ptr CreatePrinter(grpc::string *str) const = 0; +}; + +// Return the prologue of the generated header file. +grpc::string GetHeaderPrologue(File *file, const Parameters ¶ms); + +// Return the includes needed for generated header file. +grpc::string GetHeaderIncludes(File *file, const Parameters ¶ms); + +// Return the includes needed for generated source file. +grpc::string GetSourceIncludes(File *file, const Parameters ¶ms); + +// Return the epilogue of the generated header file. +grpc::string GetHeaderEpilogue(File *file, const Parameters ¶ms); + +// Return the prologue of the generated source file. +grpc::string GetSourcePrologue(File *file, const Parameters ¶ms); + +// Return the services for generated header file. +grpc::string GetHeaderServices(File *file, const Parameters ¶ms); + +// Return the services for generated source file. +grpc::string GetSourceServices(File *file, const Parameters ¶ms); + +// Return the epilogue of the generated source file. +grpc::string GetSourceEpilogue(File *file, const Parameters ¶ms); + +} // namespace grpc_cpp_generator + +#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H diff --git a/grpc/tests/grpctest.cpp b/grpc/tests/grpctest.cpp new file mode 100644 index 000000000..d1b53776b --- /dev/null +++ b/grpc/tests/grpctest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed 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 + +#include + +#include "monster_test_generated.h" +#include "monster_test.grpc.fb.h" + +using namespace MyGame::Example; + +// The callback implementation of our server, that derives from the generated +// code. It implements all rpcs specified in the FlatBuffers schema. +class ServiceImpl final : public MyGame::Example::MonsterStorage::Service { + virtual ::grpc::Status Store(::grpc::ServerContext* context, + const flatbuffers::BufferRef *request, + flatbuffers::BufferRef *response) + override { + // Create a response from the incoming request name. + fbb_.Clear(); + auto stat_offset = CreateStat(fbb_, fbb_.CreateString("Hello, " + + request->GetRoot()->name()->str())); + fbb_.Finish(stat_offset); + // Since we keep reusing the same FlatBufferBuilder, the memory it owns + // remains valid until the next call (this BufferRef doesn't own the + // memory it points to). + *response = flatbuffers::BufferRef(fbb_.GetBufferPointer(), + fbb_.GetSize()); + return grpc::Status::OK; + } + virtual ::grpc::Status Retrieve(::grpc::ServerContext *context, + const flatbuffers::BufferRef *request, + flatbuffers::BufferRef *response) + override { + assert(false); // We're not actually using this RPC. + return grpc::Status::CANCELLED; + } + + private: + flatbuffers::FlatBufferBuilder fbb_; +}; + +// Track the server instance, so we can terminate it later. +grpc::Server *server_instance = nullptr; +// Mutex to protec this variable. +std::mutex wait_for_server; +std::condition_variable server_instance_cv; + +// This function implements the server thread. +void RunServer() { + auto server_address = "0.0.0.0:50051"; + // Callback interface we implemented above. + ServiceImpl service; + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + + // Start the server. Lock to change the variable we're changing. + wait_for_server.lock(); + server_instance = builder.BuildAndStart().release(); + wait_for_server.unlock(); + server_instance_cv.notify_one(); + + std::cout << "Server listening on " << server_address << std::endl; + // This will block the thread and serve requests. + server_instance->Wait(); +} + +int main(int /*argc*/, const char * /*argv*/[]) { + // Launch server. + std::thread server_thread(RunServer); + + // wait for server to spin up. + std::unique_lock lock(wait_for_server); + while (!server_instance) server_instance_cv.wait(lock); + + // Now connect the client. + auto channel = grpc::CreateChannel("localhost:50051", + grpc::InsecureChannelCredentials()); + auto stub = MyGame::Example::MonsterStorage::NewStub(channel); + + grpc::ClientContext context; + + // Build a request with the name set. + flatbuffers::FlatBufferBuilder fbb; + auto monster_offset = CreateMonster(fbb, 0, 0, 0, fbb.CreateString("Fred")); + fbb.Finish(monster_offset); + auto request = flatbuffers::BufferRef(fbb.GetBufferPointer(), + fbb.GetSize()); + flatbuffers::BufferRef response; + + // The actual RPC. + auto status = stub->Store(&context, request, &response); + + if (status.ok()) { + auto resp = response.GetRoot()->id(); + std::cout << "RPC response: " << resp->str() << std::endl; + } else { + std::cout << "RPC failed" << std::endl; + } + + server_instance->Shutdown(); + + server_thread.join(); + + delete server_instance; + + return 0; +} + diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index ae50d57d6..6755ec279 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -1334,6 +1334,29 @@ class Verifier FLATBUFFERS_FINAL_CLASS { size_t max_tables_; }; +// Convenient way to bundle a buffer and its length, to pass it around +// typed by its root. +// A BufferRef does not own its buffer. +struct BufferRefBase {}; // for std::is_base_of +template struct BufferRef : BufferRefBase { + BufferRef() : buf(nullptr), len(0), must_free(false) {} + BufferRef(uint8_t *_buf, uoffset_t _len) + : buf(_buf), len(_len), must_free(false) {} + + ~BufferRef() { if (must_free) free(buf); } + + const T *GetRoot() const { return flatbuffers::GetRoot(buf); } + + bool Verify() { + Verifier verifier(buf, len); + return verifier.VerifyBuffer(); + } + + uint8_t *buf; + uoffset_t len; + bool must_free; +}; + // "structs" are flat structures that do not have an offset table, thus // always have all members present and do not support forwards/backwards // compatible extensions. diff --git a/include/flatbuffers/grpc.h b/include/flatbuffers/grpc.h new file mode 100644 index 000000000..52a64408d --- /dev/null +++ b/include/flatbuffers/grpc.h @@ -0,0 +1,72 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed 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. + */ + +#ifndef FLATBUFFERS_GRPC_H_ +#define FLATBUFFERS_GRPC_H_ + +// Helper functionality to glue FlatBuffers and GRPC. + +#include "grpc++/support/byte_buffer.h" +#include "grpc/byte_buffer_reader.h" + +namespace grpc { + +template +class SerializationTraits::value>::type> { + public: + // The type we're passing here is a BufferRef, which is already serialized + // FlatBuffer data, which then gets passed to GRPC. + static grpc::Status Serialize(const T& msg, + grpc_byte_buffer **buffer, + bool *own_buffer) { + // TODO(wvo): make this work without copying. + auto slice = gpr_slice_from_copied_buffer( + reinterpret_cast(msg.buf), msg.len); + *buffer = grpc_raw_byte_buffer_create(&slice, 1); + *own_buffer = true; + return grpc::Status(); + } + + // There is no de-serialization step in FlatBuffers, so we just receive + // the data from GRPC. + static grpc::Status Deserialize(grpc_byte_buffer *buffer, + T *msg, + int max_message_size) { + // TODO(wvo): make this more efficient / zero copy when possible. + auto len = grpc_byte_buffer_length(buffer); + msg->buf = reinterpret_cast(malloc(len)); + msg->len = static_cast(len); + msg->must_free = true; + uint8_t *current = msg->buf; + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer); + gpr_slice slice; + while (grpc_byte_buffer_reader_next(&reader, &slice)) { + memcpy(current, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice)); + current += GPR_SLICE_LENGTH(slice); + gpr_slice_unref(slice); + } + GPR_ASSERT(current == msg->buf + msg->len); + grpc_byte_buffer_reader_destroy(&reader); + grpc_byte_buffer_destroy(buffer); + return grpc::Status(); + } +}; + +} // namespace grpc; + +#endif // FLATBUFFERS_GRPC_H_ diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index e3027259b..b22bc08a1 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -432,7 +432,7 @@ class Parser : public ParserState { known_attributes_["original_order"] = true; known_attributes_["nested_flatbuffer"] = true; known_attributes_["csharp_partial"] = true; - known_attributes_["stream"] = true; + known_attributes_["streaming"] = true; known_attributes_["idempotent"] = true; } @@ -679,6 +679,12 @@ extern std::string BinaryMakeRule(const Parser &parser, const std::string &path, const std::string &file_name); +// Generate GRPC interfaces. +// See idl_gen_grpc.cpp. +bool GenerateGRPC(const Parser &parser, + const std::string &path, + const std::string &file_name); + } // namespace flatbuffers #endif // FLATBUFFERS_IDL_H_ diff --git a/src/flatc.cpp b/src/flatc.cpp index d4be151c8..c7ba29e86 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -74,10 +74,14 @@ const Generator generators[] = { flatbuffers::IDLOptions::kMAX, "Generate Python files for tables/structs", flatbuffers::GeneralMakeRule }, - { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", + { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", flatbuffers::IDLOptions::kMAX, "Generate PHP files for tables/structs", flatbuffers::GeneralMakeRule }, + { flatbuffers::GenerateGRPC, nullptr, "--grpc", "GRPC", + flatbuffers::IDLOptions::kMAX, + "Generate GRPC interfaces", + flatbuffers::CPPMakeRule }, }; const char *program_name = nullptr; diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp new file mode 100644 index 000000000..0dcc316d6 --- /dev/null +++ b/src/idl_gen_grpc.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed 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. + */ + +// independent from idl_parser, since this code is not needed for most clients + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +#include "src/compiler/cpp_generator.h" + +namespace flatbuffers { + +class FlatBufMethod : public grpc_cpp_generator::Method { + public: + enum Streaming { kNone, kClient, kServer, kBiDi }; + + FlatBufMethod(const RPCCall *method) + : method_(method) { + streaming_ = kNone; + auto val = method_->attributes.Lookup("streaming"); + if (val) { + if (val->constant == "client") streaming_ = kClient; + if (val->constant == "server") streaming_ = kServer; + if (val->constant == "bidi") streaming_ = kBiDi; + } + } + + std::string name() const { return method_->name; } + + std::string GRPCType(const StructDef &sd) const { + return "flatbuffers::BufferRef<" + sd.name + ">"; + } + + std::string input_type_name() const { + return GRPCType(*method_->request); + } + std::string output_type_name() const { + return GRPCType(*method_->response); + } + + bool NoStreaming() const { return streaming_ == kNone; } + bool ClientOnlyStreaming() const { return streaming_ == kClient; } + bool ServerOnlyStreaming() const { return streaming_ == kServer; } + bool BidiStreaming() const { return streaming_ == kBiDi; } + + private: + const RPCCall *method_; + Streaming streaming_; +}; + +class FlatBufService : public grpc_cpp_generator::Service { + public: + FlatBufService(const ServiceDef *service) : service_(service) {} + + std::string name() const { return service_->name; } + + int method_count() const { + return static_cast(service_->calls.vec.size()); + }; + + std::unique_ptr method(int i) const { + return std::unique_ptr( + new FlatBufMethod(service_->calls.vec[i])); + }; + + private: + const ServiceDef *service_; +}; + +class FlatBufPrinter : public grpc_cpp_generator::Printer { + public: + FlatBufPrinter(std::string *str) + : str_(str), escape_char_('$'), indent_(0) {} + + void Print(const std::map &vars, + const char *string_template) { + std::string s = string_template; + // Replace any occurrences of strings in "vars" that are surrounded + // by the escape character by what they're mapped to. + size_t pos; + while ((pos = s.find(escape_char_)) != std::string::npos) { + // Found an escape char, must also find the closing one. + size_t pos2 = s.find(escape_char_, pos + 1); + // If placeholder not closed, ignore. + if (pos2 == std::string::npos) break; + auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1)); + // If unknown placeholder, ignore. + if (it == vars.end()) break; + // Subtitute placeholder. + s.replace(pos, pos2 - pos + 1, it->second); + } + Print(s.c_str()); + } + + void Print(const char *s) { + // Add this string, but for each part separated by \n, add indentation. + for (;;) { + // Current indentation. + str_->insert(str_->end(), indent_ * 2, ' '); + // See if this contains more than one line. + auto lf = strchr(s, '\n'); + if (lf) { + (*str_) += std::string(s, lf + 1); + s = lf + 1; + if (!*s) break; // Only continue if there's more lines. + } else { + (*str_) += s; + break; + } + } + } + + void Indent() { indent_++; } + void Outdent() { indent_--; assert(indent_ >= 0); } + + private: + std::string *str_; + char escape_char_; + int indent_; +}; + +class FlatBufFile : public grpc_cpp_generator::File { + public: + FlatBufFile(const Parser &parser, const std::string &file_name) + : parser_(parser), file_name_(file_name) {} + + std::string filename() const { return file_name_; } + std::string filename_without_ext() const { + return StripExtension(file_name_); + } + + std::string message_header_ext() const { return "_generated.h"; } + std::string service_header_ext() const { return ".grpc.fb.h"; } + + std::string package() const { + return parser_.namespaces_.back()->GetFullyQualifiedName(""); + } + + std::vector package_parts() const { + return parser_.namespaces_.back()->components; + } + + std::string additional_headers() const { + return "#include \"flatbuffers/grpc.h\"\n"; + } + + int service_count() const { + return static_cast(parser_.services_.vec.size()); + }; + + std::unique_ptr service(int i) const { + return std::unique_ptr ( + new FlatBufService(parser_.services_.vec[i])); + } + + std::unique_ptr CreatePrinter(std::string *str) const { + return std::unique_ptr( + new FlatBufPrinter(str)); + } + + private: + const Parser &parser_; + const std::string &file_name_; +}; + +bool GenerateGRPC(const Parser &parser, + const std::string &/*path*/, + const std::string &file_name) { + + int nservices = 0; + for (auto it = parser.services_.vec.begin(); + it != parser.services_.vec.end(); ++it) { + if (!(*it)->generated) nservices++; + } + if (!nservices) return true; + + grpc_cpp_generator::Parameters generator_parameters; + // TODO(wvo): make the other parameters in this struct configurable. + generator_parameters.use_system_headers = true; + + FlatBufFile fbfile(parser, file_name); + + std::string header_code = + grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters); + + std::string source_code = + grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) + + grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) + + grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) + + grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters); + + return flatbuffers::SaveFile((file_name + ".grpc.fb.h").c_str(), + header_code, false) && + flatbuffers::SaveFile((file_name + ".grpc.fb.cc").c_str(), + source_code, false); +} + +} // namespace flatbuffers + diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 8fddb3c66..b758e9592 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -1423,6 +1423,10 @@ void Parser::MarkGenerated() { it != structs_.vec.end(); ++it) { (*it)->generated = true; } + for (auto it = services_.vec.begin(); + it != services_.vec.end(); ++it) { + (*it)->generated = true; + } } CheckedError Parser::ParseNamespace() { diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 00b16d900..53d54707b 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -12,6 +12,6 @@ :: See the License for the specific language governing permissions and :: limitations under the License. -..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --no-includes monster_test.fbs monsterdata_test.json ..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test\namespace_test1.fbs namespace_test\namespace_test2.fbs ..\flatc.exe --binary --schema monster_test.fbs diff --git a/tests/generate_code.sh b/tests/generate_code.sh index b74c2bec5..3436d8586 100644 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --no-includes monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --binary --schema monster_test.fbs diff --git a/tests/monster_test.fbs b/tests/monster_test.fbs index 7ddaa582f..3fecd33cf 100755 --- a/tests/monster_test.fbs +++ b/tests/monster_test.fbs @@ -70,7 +70,7 @@ table Monster { } rpc_service MonsterStorage { - Store(Monster):Stat (stream); + Store(Monster):Stat (streaming: "none"); Retrieve(Stat):Monster (idempotent); } diff --git a/tests/monster_test.grpc.fb.cc b/tests/monster_test.grpc.fb.cc new file mode 100644 index 000000000..71dbd2d4f --- /dev/null +++ b/tests/monster_test.grpc.fb.cc @@ -0,0 +1,85 @@ +// Generated by the gRPC protobuf plugin. +// If you make any local change, they will be lost. +// source: monster_test + +#include "monster_test_generated.h" +#include "monster_test.grpc.fb.h" +#include "flatbuffers/grpc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +namespace MyGame { +namespace Example { + +static const char* MonsterStorage_method_names[] = { + "/MyGame.Example..MonsterStorage/Store", + "/MyGame.Example..MonsterStorage/Retrieve", +}; + +std::unique_ptr< MonsterStorage::Stub> MonsterStorage::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { + std::unique_ptr< MonsterStorage::Stub> stub(new MonsterStorage::Stub(channel)); + return stub; +} + +MonsterStorage::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel) + : channel_(channel) , rpcmethod_Store_(MonsterStorage_method_names[0], ::grpc::RpcMethod::NORMAL_RPC, channel) + , rpcmethod_Retrieve_(MonsterStorage_method_names[1], ::grpc::RpcMethod::NORMAL_RPC, channel) + {} + +::grpc::Status MonsterStorage::Stub::Store(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, flatbuffers::BufferRef* response) { + return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_Store_, context, request, response); +} + +::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>* MonsterStorage::Stub::AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) { + return new ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>(channel_.get(), cq, rpcmethod_Store_, context, request); +} + +::grpc::Status MonsterStorage::Stub::Retrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, flatbuffers::BufferRef* response) { + return ::grpc::BlockingUnaryCall(channel_.get(), rpcmethod_Retrieve_, context, request, response); +} + +::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>* MonsterStorage::Stub::AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) { + return new ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>(channel_.get(), cq, rpcmethod_Retrieve_, context, request); +} + +MonsterStorage::Service::Service() { + (void)MonsterStorage_method_names; + AddMethod(new ::grpc::RpcServiceMethod( + MonsterStorage_method_names[0], + ::grpc::RpcMethod::NORMAL_RPC, + new ::grpc::RpcMethodHandler< MonsterStorage::Service, flatbuffers::BufferRef, flatbuffers::BufferRef>( + std::mem_fn(&MonsterStorage::Service::Store), this))); + AddMethod(new ::grpc::RpcServiceMethod( + MonsterStorage_method_names[1], + ::grpc::RpcMethod::NORMAL_RPC, + new ::grpc::RpcMethodHandler< MonsterStorage::Service, flatbuffers::BufferRef, flatbuffers::BufferRef>( + std::mem_fn(&MonsterStorage::Service::Retrieve), this))); +} + +MonsterStorage::Service::~Service() { +} + +::grpc::Status MonsterStorage::Service::Store(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + +::grpc::Status MonsterStorage::Service::Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response) { + (void) context; + (void) request; + (void) response; + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); +} + + +} // namespace MyGame +} // namespace Example + diff --git a/tests/monster_test.grpc.fb.h b/tests/monster_test.grpc.fb.h new file mode 100644 index 000000000..4269234f3 --- /dev/null +++ b/tests/monster_test.grpc.fb.h @@ -0,0 +1,155 @@ +// Generated by the gRPC protobuf plugin. +// If you make any local change, they will be lost. +// source: monster_test +#ifndef GRPC_monster_5ftest__INCLUDED +#define GRPC_monster_5ftest__INCLUDED + +#include "monster_test_generated.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { +class CompletionQueue; +class Channel; +class RpcService; +class ServerCompletionQueue; +class ServerContext; +} // namespace grpc + +namespace MyGame { +namespace Example { + +class MonsterStorage GRPC_FINAL { + public: + class StubInterface { + public: + virtual ~StubInterface() {} + virtual ::grpc::Status Store(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, flatbuffers::BufferRef* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef>> AsyncStore(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef>>(AsyncStoreRaw(context, request, cq)); + } + virtual ::grpc::Status Retrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, flatbuffers::BufferRef* response) = 0; + std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef>> AsyncRetrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef>>(AsyncRetrieveRaw(context, request, cq)); + } + private: + virtual ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef>* AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) = 0; + virtual ::grpc::ClientAsyncResponseReaderInterface< flatbuffers::BufferRef>* AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) = 0; + }; + class Stub GRPC_FINAL : public StubInterface { + public: + Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel); + ::grpc::Status Store(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, flatbuffers::BufferRef* response) GRPC_OVERRIDE; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>> AsyncStore(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>>(AsyncStoreRaw(context, request, cq)); + } + ::grpc::Status Retrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, flatbuffers::BufferRef* response) GRPC_OVERRIDE; + std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>> AsyncRetrieve(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) { + return std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>>(AsyncRetrieveRaw(context, request, cq)); + } + + private: + std::shared_ptr< ::grpc::ChannelInterface> channel_; + ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>* AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE; + ::grpc::ClientAsyncResponseReader< flatbuffers::BufferRef>* AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::BufferRef& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE; + const ::grpc::RpcMethod rpcmethod_Store_; + const ::grpc::RpcMethod rpcmethod_Retrieve_; + }; + static std::unique_ptr NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); + + class Service : public ::grpc::Service { + public: + Service(); + virtual ~Service(); + virtual ::grpc::Status Store(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response); + virtual ::grpc::Status Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response); + }; + template + class WithAsyncMethod_Store : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithAsyncMethod_Store() { + ::grpc::Service::MarkMethodAsync(0); + } + ~WithAsyncMethod_Store() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Store(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestStore(::grpc::ServerContext* context, flatbuffers::BufferRef* request, ::grpc::ServerAsyncResponseWriter< flatbuffers::BufferRef>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); + } + }; + template + class WithAsyncMethod_Retrieve : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithAsyncMethod_Retrieve() { + ::grpc::Service::MarkMethodAsync(1); + } + ~WithAsyncMethod_Retrieve() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + void RequestRetrieve(::grpc::ServerContext* context, flatbuffers::BufferRef* request, ::grpc::ServerAsyncResponseWriter< flatbuffers::BufferRef>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { + ::grpc::Service::RequestAsyncUnary(1, context, request, response, new_call_cq, notification_cq, tag); + } + }; + typedef WithAsyncMethod_Store< WithAsyncMethod_Retrieve< Service > > AsyncService; + template + class WithGenericMethod_Store : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithGenericMethod_Store() { + ::grpc::Service::MarkMethodGeneric(0); + } + ~WithGenericMethod_Store() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Store(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; + template + class WithGenericMethod_Retrieve : public BaseClass { + private: + void BaseClassMustBeDerivedFromService(const Service *service) {} + public: + WithGenericMethod_Retrieve() { + ::grpc::Service::MarkMethodGeneric(1); + } + ~WithGenericMethod_Retrieve() GRPC_OVERRIDE { + BaseClassMustBeDerivedFromService(this); + } + // disable synchronous version of this method + ::grpc::Status Retrieve(::grpc::ServerContext* context, const flatbuffers::BufferRef* request, flatbuffers::BufferRef* response) GRPC_FINAL GRPC_OVERRIDE { + abort(); + return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); + } + }; +}; + +} // namespace Example +} // namespace MyGame + + +#endif // GRPC_monster_5ftest__INCLUDED