forked from BigfootDev/flatbuffers
gRPC callbackService support added (#8666)
* grpc callbackService support added Signed-off-by: shankeleven <shashanksati11@gmail.com> * tests: regenerate C++ gRPC golden with --grpc-callback-api (CallbackService & async_ reactor APIs); update formatting and method placement --------- Signed-off-by: shankeleven <shashanksati11@gmail.com> Co-authored-by: Wouter van Oortmerssen <aardappel@gmail.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
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
|
||||
@@ -39,4 +38,64 @@ For Bazel users:
|
||||
|
||||
```shell
|
||||
$bazel test tests/...
|
||||
```
|
||||
```
|
||||
|
||||
## C++ Callback API Generation
|
||||
|
||||
FlatBuffers gRPC C++ code generation now optionally supports the modern gRPC Callback API.
|
||||
|
||||
To enable generation of a `CallbackService` skeleton alongside the existing `Service` and async mixins, invoke `flatc` with both `--grpc` and `--grpc-callback-api`:
|
||||
|
||||
```shell
|
||||
flatc --cpp --grpc --grpc-callback-api your_service.fbs
|
||||
```
|
||||
|
||||
This adds (guarded by `#if defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)`) a class:
|
||||
|
||||
```cpp
|
||||
class YourService::CallbackService : public ::grpc::Service { /* reactor virtuals */ };
|
||||
```
|
||||
|
||||
Each RPC shape maps to the appropriate reactor return type:
|
||||
|
||||
- Unary -> `::grpc::ServerUnaryReactor*` Method(...)
|
||||
- Client streaming -> `::grpc::ServerReadReactor<Request>*`
|
||||
- Server streaming -> `::grpc::ServerWriteReactor<Response>*`
|
||||
- Bidi streaming -> `::grpc::ServerBidiReactor<Request, Response>*`
|
||||
|
||||
Default generated implementations return `nullptr`; override in your derived class and return a reactor instance you manage (see gRPC docs for lifecycle patterns).
|
||||
|
||||
If your gRPC library predates the stable callback API macro, the code inside the guard will be skipped (no breaking changes). Ensure you build against a recent gRPC (1.38+; verify current minimum in grpc repo) to use this feature.
|
||||
|
||||
### Client Callback Stubs
|
||||
|
||||
When `--grpc-callback-api` is supplied, the generated C++ client stub gains native callback / reactor based async methods in addition to the existing synchronous / generic async flavors, guarded by the same macro. For each RPC named `Foo`:
|
||||
|
||||
Unary:
|
||||
|
||||
```
|
||||
void async_Foo(::grpc::ClientContext*, const Request&, Response*, std::function<void(::grpc::Status)>);
|
||||
void async_Foo(::grpc::ClientContext*, const Request&, Response*, ::grpc::ClientUnaryReactor*);
|
||||
```
|
||||
|
||||
Client streaming:
|
||||
|
||||
```
|
||||
::grpc::ClientWriteReactor<Request>* async_Foo(::grpc::ClientContext*, Response*, ::grpc::ClientWriteReactor<Request>*);
|
||||
```
|
||||
|
||||
Server streaming:
|
||||
|
||||
```
|
||||
::grpc::ClientReadReactor<Response>* async_Foo(::grpc::ClientContext*, const Request&, ::grpc::ClientReadReactor<Response>*);
|
||||
```
|
||||
|
||||
Bidirectional streaming:
|
||||
|
||||
```
|
||||
::grpc::ClientBidiReactor<Request, Response>* async_Foo(::grpc::ClientContext*, ::grpc::ClientBidiReactor<Request, Response>*);
|
||||
```
|
||||
|
||||
These map directly onto the native gRPC callback API factories (e.g. `CallbackUnaryCall`, `ClientCallbackWriterFactory::Create`, etc.) and do not spawn threads. Override the appropriate reactor callbacks per gRPC's documentation to drive I/O.
|
||||
|
||||
If your build uses an older gRPC lacking the non-experimental macro, these symbols will not be emitted, preserving backwards compatibility.
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
namespace grpc_cpp_generator {
|
||||
namespace {
|
||||
|
||||
template<class T>
|
||||
static grpc::string as_string(T x) {
|
||||
template<class T> static grpc::string as_string(T x) {
|
||||
std::ostringstream out;
|
||||
out << x;
|
||||
return out.str();
|
||||
@@ -39,12 +38,13 @@ static grpc::string FilenameIdentifier(const grpc::string &filename) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, size_t N>
|
||||
static T *array_end(T (&array)[N]) { return array + N; }
|
||||
template<class T, size_t N> static T *array_end(T (&array)[N]) {
|
||||
return array + N;
|
||||
}
|
||||
|
||||
static void PrintIncludes(grpc_generator::Printer *printer,
|
||||
const std::vector<grpc::string> &headers,
|
||||
const Parameters ¶ms) {
|
||||
const std::vector<grpc::string> &headers,
|
||||
const Parameters ¶ms) {
|
||||
std::map<grpc::string, grpc::string> vars;
|
||||
|
||||
vars["l"] = params.use_system_headers ? '<' : '"';
|
||||
@@ -60,6 +60,18 @@ static void PrintIncludes(grpc_generator::Printer *printer,
|
||||
vars["h"] = *i;
|
||||
printer->Print(vars, "#include $l$$h$$r$\n");
|
||||
}
|
||||
if (params.generate_callback_api) {
|
||||
// Callback API headers (guarded later by feature macro in emitted code).
|
||||
static const char *cb_headers[] = {
|
||||
"grpcpp/impl/codegen/callback_common.h",
|
||||
"grpcpp/impl/codegen/server_callback_handlers.h",
|
||||
"grpcpp/support/client_callback.h"
|
||||
};
|
||||
for (auto &h : cb_headers) {
|
||||
vars["h"] = h;
|
||||
printer->Print(vars, "#include $l$$h$$r$\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -138,7 +150,6 @@ grpc::string GetHeaderIncludes(grpc_generator::File *file,
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static void PrintHeaderClientMethodInterfaces(
|
||||
@@ -355,12 +366,10 @@ static void PrintHeaderClientMethodInterfaces(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars,
|
||||
bool is_public) {
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars,
|
||||
bool is_public) {
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
@@ -377,6 +386,22 @@ static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
*vars,
|
||||
"::grpc::Status $Method$(::grpc::ClientContext* context, "
|
||||
"const $Request$& request, $Response$* response) override;\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
// Native gRPC callback unary wrappers (function callback & reactor
|
||||
// variants).
|
||||
printer->Print(*vars,
|
||||
"// Callback unary (function form). Request/response "
|
||||
"must outlive callback.\n");
|
||||
printer->Print(*vars,
|
||||
"void async_$Method$(::grpc::ClientContext* context, "
|
||||
"const $Request$& request, $Response$* response, "
|
||||
"std::function<void(::grpc::Status)> on_done);\n");
|
||||
printer->Print(*vars, "// Callback unary (reactor form).\n");
|
||||
printer->Print(*vars,
|
||||
"void async_$Method$(::grpc::ClientContext* context, "
|
||||
"const $Request$& request, $Response$* response, "
|
||||
"::grpc::ClientUnaryReactor* reactor);\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -407,6 +432,12 @@ static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
"($Method$Raw(context, response));\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(*vars, "// Client streaming callback reactor entry.\n");
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void async_$Method$(::grpc::ClientContext* context, $Response$* response, ::grpc::ClientWriteReactor< $Request$ >* reactor);\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -440,6 +471,11 @@ static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
"($Method$Raw(context, request));\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(*vars, "// Server streaming callback reactor entry.\n");
|
||||
printer->Print(*vars,
|
||||
"void async_$Method$(::grpc::ClientContext* context, const $Request$& request, ::grpc::ClientReadReactor< $Response$ >* reactor);\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -472,6 +508,12 @@ static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
"$Method$Raw(context));\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(*vars, "// Bidirectional streaming callback reactor entry.\n");
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void async_$Method$(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< $Request$, $Response$ >* reactor);\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -506,6 +548,10 @@ static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
"const $Request$& request, "
|
||||
"::grpc::CompletionQueue* cq) override;\n");
|
||||
}
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
// Native callback unary forms declared earlier (no private sync helper
|
||||
// needed).
|
||||
}
|
||||
} else if (ClientOnlyStreaming(method)) {
|
||||
printer->Print(*vars,
|
||||
"::grpc::ClientWriter< $Request$>* $Method$Raw("
|
||||
@@ -560,17 +606,17 @@ static void PrintHeaderClientMethod(grpc_generator::Printer *printer,
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintHeaderClientMethodData(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
static void PrintHeaderClientMethodData(
|
||||
grpc_generator::Printer *printer, const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Method"] = method->name();
|
||||
printer->Print(*vars,
|
||||
"const ::grpc::internal::RpcMethod rpcmethod_$Method$_;\n");
|
||||
}
|
||||
|
||||
static void PrintHeaderServerMethodSync(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
static void PrintHeaderServerMethodSync(
|
||||
grpc_generator::Printer *printer, const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
@@ -602,9 +648,9 @@ static void PrintHeaderServerMethodSync(grpc_generator::Printer *printer,
|
||||
printer->Print(method->GetTrailingComments("//").c_str());
|
||||
}
|
||||
|
||||
static void PrintHeaderServerMethodAsync(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
static void PrintHeaderServerMethodAsync(
|
||||
grpc_generator::Printer *printer, const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
@@ -894,8 +940,8 @@ static void PrintHeaderServerMethodGeneric(
|
||||
}
|
||||
|
||||
static void PrintHeaderService(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Service *service,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
const grpc_generator::Service *service,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Service"] = service->name();
|
||||
|
||||
printer->Print(service->GetLeadingComments("//").c_str());
|
||||
@@ -930,6 +976,10 @@ static void PrintHeaderService(grpc_generator::Printer *printer,
|
||||
false);
|
||||
}
|
||||
printer->Outdent();
|
||||
// Forward declaration of nested CallbackService if callback API enabled.
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print("class CallbackService;\n");
|
||||
}
|
||||
printer->Print("};\n");
|
||||
printer->Print(
|
||||
"class Stub final : public StubInterface"
|
||||
@@ -1062,9 +1112,51 @@ static void PrintHeaderService(grpc_generator::Printer *printer,
|
||||
printer->Outdent();
|
||||
printer->Print("};\n");
|
||||
printer->Print(service->GetTrailingComments("//").c_str());
|
||||
|
||||
// Optional CallbackService (modern async API)
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
(*vars)["Service"] = service->name();
|
||||
printer->Print("\n#if defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)\n");
|
||||
printer->Print(*vars,
|
||||
"class $Service$::CallbackService : public ::grpc::Service "
|
||||
"{\n public:\n CallbackService();\n virtual "
|
||||
"~CallbackService();\n");
|
||||
printer->Indent();
|
||||
for (int i = 0; i < service->method_count(); ++i) {
|
||||
auto m = service->method(i);
|
||||
(*vars)["Method"] = m->name();
|
||||
(*vars)["Request"] = m->input_type_name();
|
||||
(*vars)["Response"] = m->output_type_name();
|
||||
if (m->NoStreaming()) {
|
||||
printer->Print(*vars,
|
||||
"virtual ::grpc::ServerUnaryReactor* "
|
||||
"$Method$(::grpc::CallbackServerContext* context, const "
|
||||
"$Request$* request, $Response$* response);\n");
|
||||
} else if (ClientOnlyStreaming(m.get())) {
|
||||
printer->Print(*vars,
|
||||
"virtual ::grpc::ServerReadReactor<$Request$>* "
|
||||
"$Method$(::grpc::CallbackServerContext* context, "
|
||||
"$Response$* response);\n");
|
||||
} else if (ServerOnlyStreaming(m.get())) {
|
||||
printer->Print(*vars,
|
||||
"virtual ::grpc::ServerWriteReactor<$Response$>* "
|
||||
"$Method$(::grpc::CallbackServerContext* context, const "
|
||||
"$Request$* request);\n");
|
||||
} else if (m->BidiStreaming()) {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"virtual ::grpc::ServerBidiReactor<$Request$, $Response$>* "
|
||||
"$Method$(::grpc::CallbackServerContext* context);\n");
|
||||
}
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print(
|
||||
"};\n#else\n// Callback API requested but not available in this gRPC "
|
||||
"version.\n#endif // GRPC_CALLBACK_API_NONEXPERIMENTAL\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
grpc::string GetHeaderServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms) {
|
||||
@@ -1084,9 +1176,14 @@ grpc::string GetHeaderServices(grpc_generator::File *file,
|
||||
}
|
||||
|
||||
for (int i = 0; i < file->service_count(); ++i) {
|
||||
vars["generate_callback_api"] = params.generate_callback_api ? "1" : "0";
|
||||
PrintHeaderService(printer.get(), file->service(i).get(), &vars);
|
||||
printer->Print("\n");
|
||||
}
|
||||
if (params.generate_callback_api) {
|
||||
printer->Print("// FlatBuffers: Callback API code generated.\n");
|
||||
printer->Print("#define FLATBUFFERS_GENERATED_GRPC_CALLBACK_API 1\n\n");
|
||||
}
|
||||
|
||||
if (!params.services_namespace.empty()) {
|
||||
printer->Print(vars, "} // namespace $services_namespace$\n\n");
|
||||
@@ -1139,7 +1236,8 @@ grpc::string GetSourcePrologue(grpc_generator::File *file,
|
||||
|
||||
printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
|
||||
printer->Print(vars,
|
||||
"// If you make any local change, they will be lost.\n");
|
||||
"// FlatBuffers modified generator: native gRPC callback "
|
||||
"client API enabled when --grpc-callback-api.\n");
|
||||
printer->Print(vars, "// source: $filename$\n\n");
|
||||
|
||||
printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
|
||||
@@ -1184,12 +1282,11 @@ grpc::string GetSourceIncludes(grpc_generator::File *file,
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static void PrintSourceClientMethod(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
static void PrintSourceClientMethod(
|
||||
grpc_generator::Printer *printer, const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
@@ -1209,6 +1306,27 @@ static void PrintSourceClientMethod(grpc_generator::Printer *printer,
|
||||
" return ::grpc::internal::BlockingUnaryCall"
|
||||
"(channel_.get(), rpcmethod_$Method$_, "
|
||||
"context, request, response);\n}\n\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void $ns$$Service$::Stub::async_$Method$(::grpc::ClientContext* "
|
||||
"context, const $Request$& request, $Response$* response, "
|
||||
"std::function<void(::grpc::Status)> on_done) {\n");
|
||||
printer->Print(*vars,
|
||||
" ::grpc::internal::CallbackUnaryCall(channel_.get(), "
|
||||
"rpcmethod_$Method$_, context, &request, response, "
|
||||
"std::move(on_done));\n}\n\n");
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void $ns$$Service$::Stub::async_$Method$(::grpc::ClientContext* "
|
||||
"context, const $Request$& request, $Response$* response, "
|
||||
"::grpc::ClientUnaryReactor* reactor) {\n");
|
||||
printer->Print(
|
||||
*vars,
|
||||
" "
|
||||
"::grpc::internal::ClientCallbackUnaryFactory::Create(channel_.get(),"
|
||||
" rpcmethod_$Method$_, context, &request, response, reactor);\n}\n\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -1241,6 +1359,17 @@ static void PrintSourceClientMethod(grpc_generator::Printer *printer,
|
||||
"rpcmethod_$Method$_, "
|
||||
"context, response);\n"
|
||||
"}\n\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void $ns$$Service$::Stub::async_$Method$(::grpc::ClientContext* "
|
||||
"context, $Response$* response, ::grpc::ClientWriteReactor< "
|
||||
"$Request$ >* reactor) {\n");
|
||||
printer->Print(*vars,
|
||||
" ::grpc::internal::ClientCallbackWriterFactory< "
|
||||
"$Request$ >::Create(channel_.get(), rpcmethod_$Method$_, "
|
||||
"context, response, reactor);\n}\n\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -1274,6 +1403,17 @@ static void PrintSourceClientMethod(grpc_generator::Printer *printer,
|
||||
"rpcmethod_$Method$_, "
|
||||
"context, request);\n"
|
||||
"}\n\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void $ns$$Service$::Stub::async_$Method$(::grpc::ClientContext* "
|
||||
"context, const $Request$& request, ::grpc::ClientReadReactor< "
|
||||
"$Response$ >* reactor) {\n");
|
||||
printer->Print(*vars,
|
||||
" ::grpc::internal::ClientCallbackReaderFactory< "
|
||||
"$Response$ >::Create(channel_.get(), "
|
||||
"rpcmethod_$Method$_, context, &request, reactor);\n}\n\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -1307,6 +1447,17 @@ static void PrintSourceClientMethod(grpc_generator::Printer *printer,
|
||||
"rpcmethod_$Method$_, "
|
||||
"context);\n"
|
||||
"}\n\n");
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"void $ns$$Service$::Stub::async_$Method$(::grpc::ClientContext* "
|
||||
"context, ::grpc::ClientBidiReactor< $Request$, $Response$ >* "
|
||||
"reactor) {\n");
|
||||
printer->Print(*vars,
|
||||
" ::grpc::internal::ClientCallbackReaderWriterFactory< "
|
||||
"$Request$, $Response$ >::Create(channel_.get(), "
|
||||
"rpcmethod_$Method$_, context, reactor);\n}\n\n");
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(async_prefixes) / sizeof(async_prefixes[0]);
|
||||
i++) {
|
||||
auto &async_prefix = async_prefixes[i];
|
||||
@@ -1331,9 +1482,9 @@ static void PrintSourceClientMethod(grpc_generator::Printer *printer,
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintSourceServerMethod(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
static void PrintSourceServerMethod(
|
||||
grpc_generator::Printer *printer, const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
@@ -1381,8 +1532,8 @@ static void PrintSourceServerMethod(grpc_generator::Printer *printer,
|
||||
}
|
||||
|
||||
static void PrintSourceService(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Service *service,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
const grpc_generator::Service *service,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Service"] = service->name();
|
||||
|
||||
if (service->method_count() > 0) {
|
||||
@@ -1495,9 +1646,110 @@ static void PrintSourceService(grpc_generator::Printer *printer,
|
||||
(*vars)["Idx"] = as_string(i);
|
||||
PrintSourceServerMethod(printer, service->method(i).get(), vars);
|
||||
}
|
||||
|
||||
// CallbackService implementation (if enabled)
|
||||
if ((*vars)["generate_callback_api"] == "1") {
|
||||
(*vars)["Service"] = service->name();
|
||||
printer->Print("#if defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)\n");
|
||||
printer->Print(*vars,
|
||||
"$ns$$Service$::CallbackService::CallbackService() {\n");
|
||||
printer->Indent();
|
||||
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::internal::RpcServiceMethod(\n"
|
||||
" $prefix$$Service$_method_names[$Idx$],\n"
|
||||
" ::grpc::internal::RpcMethod::NORMAL_RPC,\n"
|
||||
" new ::grpc::internal::CallbackUnaryHandler<$Request$, $Response$>(\n"
|
||||
" [this](::grpc::CallbackServerContext* ctx, const $Request$* req, $Response$* resp) {\n"
|
||||
" return this->$Method$(ctx, req, resp);\n"
|
||||
" })));\n");
|
||||
} else if (ClientOnlyStreaming(method.get())) {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
|
||||
" $prefix$$Service$_method_names[$Idx$],\n"
|
||||
" ::grpc::internal::RpcMethod::CLIENT_STREAMING,\n"
|
||||
" new ::grpc::internal::CallbackClientStreamingHandler<$Request$, $Response$>(\n"
|
||||
" [this](::grpc::CallbackServerContext* ctx, $Response$* resp) {\n"
|
||||
" return this->$Method$(ctx, resp);\n"
|
||||
" })));\n");
|
||||
} else if (ServerOnlyStreaming(method.get())) {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
|
||||
" $prefix$$Service$_method_names[$Idx$],\n"
|
||||
" ::grpc::internal::RpcMethod::SERVER_STREAMING,\n"
|
||||
" new ::grpc::internal::CallbackServerStreamingHandler<$Request$, $Response$>(\n"
|
||||
" [this](::grpc::CallbackServerContext* ctx, const $Request$* req) {\n"
|
||||
" return this->$Method$(ctx, req);\n"
|
||||
" })));\n");
|
||||
} else if (method->BidiStreaming()) {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
|
||||
" $prefix$$Service$_method_names[$Idx$],\n"
|
||||
" ::grpc::internal::RpcMethod::BIDI_STREAMING,\n"
|
||||
" new ::grpc::internal::CallbackBidiHandler<$Request$, $Response$>(\n"
|
||||
" [this](::grpc::CallbackServerContext* ctx) {\n"
|
||||
" return this->$Method$(ctx);\n"
|
||||
" })));\n");
|
||||
}
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
printer->Print(*vars,
|
||||
"$ns$$Service$::CallbackService::~CallbackService() {}\n\n");
|
||||
// Default method bodies returning UNIMPLEMENTED reactors.
|
||||
for (int i = 0; i < service->method_count(); ++i) {
|
||||
auto method = service->method(i);
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
if (method->NoStreaming()) {
|
||||
printer->Print(*vars,
|
||||
"::grpc::ServerUnaryReactor* "
|
||||
"$ns$$Service$::CallbackService::$Method$(::grpc::"
|
||||
"CallbackServerContext* /*context*/, const $Request$* "
|
||||
"/*request*/, $Response$* /*response*/) {\n"
|
||||
" return nullptr; // user must override\n"
|
||||
"}\n\n");
|
||||
} else if (ClientOnlyStreaming(method.get())) {
|
||||
printer->Print(
|
||||
*vars,
|
||||
"::grpc::ServerReadReactor<$Request$>* "
|
||||
"$ns$$Service$::CallbackService::$Method$(::grpc::"
|
||||
"CallbackServerContext* /*context*/, $Response$* /*response*/) {\n"
|
||||
" return nullptr; // user must override\n"
|
||||
"}\n\n");
|
||||
} else if (ServerOnlyStreaming(method.get())) {
|
||||
printer->Print(*vars,
|
||||
"::grpc::ServerWriteReactor<$Response$>* "
|
||||
"$ns$$Service$::CallbackService::$Method$(::grpc::"
|
||||
"CallbackServerContext* /*context*/, const $Request$* "
|
||||
"/*request*/) {\n"
|
||||
" return nullptr; // user must override\n"
|
||||
"}\n\n");
|
||||
} else if (method->BidiStreaming()) {
|
||||
printer->Print(*vars,
|
||||
"::grpc::ServerBidiReactor<$Request$, $Response$>* "
|
||||
"$ns$$Service$::CallbackService::$Method$(::grpc::"
|
||||
"CallbackServerContext* /*context*/) {\n"
|
||||
" return nullptr; // user must override\n"
|
||||
"}\n\n");
|
||||
}
|
||||
}
|
||||
printer->Print("#endif // GRPC_CALLBACK_API_NONEXPERIMENTAL\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
grpc::string GetSourceServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms) {
|
||||
@@ -1519,6 +1771,7 @@ grpc::string GetSourceServices(grpc_generator::File *file,
|
||||
}
|
||||
|
||||
for (int i = 0; i < file->service_count(); ++i) {
|
||||
vars["generate_callback_api"] = params.generate_callback_api ? "1" : "0";
|
||||
PrintSourceService(printer.get(), file->service(i).get(), &vars);
|
||||
printer->Print("\n");
|
||||
}
|
||||
@@ -1601,12 +1854,11 @@ grpc::string GetMockIncludes(grpc_generator::File *file,
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static void PrintMockClientMethods(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
const grpc_generator::Method *method,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Method"] = method->name();
|
||||
(*vars)["Request"] = method->input_type_name();
|
||||
(*vars)["Response"] = method->output_type_name();
|
||||
@@ -1697,8 +1949,8 @@ static void PrintMockClientMethods(grpc_generator::Printer *printer,
|
||||
}
|
||||
|
||||
static void PrintMockService(grpc_generator::Printer *printer,
|
||||
const grpc_generator::Service *service,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
const grpc_generator::Service *service,
|
||||
std::map<grpc::string, grpc::string> *vars) {
|
||||
(*vars)["Service"] = service->name();
|
||||
|
||||
printer->Print(*vars,
|
||||
@@ -1712,7 +1964,7 @@ static void PrintMockService(grpc_generator::Printer *printer,
|
||||
printer->Print("};\n");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
grpc::string GetMockServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms) {
|
||||
|
||||
@@ -37,6 +37,8 @@ struct Parameters {
|
||||
std::string message_header_extension;
|
||||
// Default: ".grpc.fb.h"
|
||||
std::string service_header_extension;
|
||||
// Generate modern callback-based async API service code (CallbackService)
|
||||
bool generate_callback_api = false;
|
||||
};
|
||||
|
||||
// Return the prologue of the generated header file.
|
||||
|
||||
@@ -17,3 +17,27 @@ cc_test(
|
||||
"@com_github_grpc_grpc//:grpc++",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "grpc_callback_compile_test",
|
||||
srcs = ["grpctest_callback_compile.cpp"],
|
||||
copts = ["-Itests"],
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//tests:monster_test_cc_fbs",
|
||||
"//tests:monster_test_grpc",
|
||||
"@com_github_grpc_grpc//:grpc++",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "grpc_callback_client_compile_test",
|
||||
srcs = ["grpctest_callback_client_compile.cpp"],
|
||||
copts = ["-Itests"],
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//tests:monster_test_cc_fbs",
|
||||
"//tests:monster_test_grpc",
|
||||
"@com_github_grpc_grpc//:grpc++",
|
||||
],
|
||||
)
|
||||
|
||||
61
grpc/tests/grpctest_callback_client_compile.cpp
Normal file
61
grpc/tests/grpctest_callback_client_compile.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Verifies that the generated gRPC callback client stub methods are present.
|
||||
// This is a compile-time only test: it never performs an actual RPC.
|
||||
// It supplements grpctest_callback_compile.cpp (server side) by checking
|
||||
// client-side async_ / reactor entry points.
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "monster_test.grpc.fb.h"
|
||||
|
||||
#if defined(FLATBUFFERS_GENERATED_GRPC_CALLBACK_API) && \
|
||||
defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)
|
||||
using Stub = MyGame::Example::MonsterStorage::Stub;
|
||||
using namespace MyGame::Example; // NOLINT
|
||||
|
||||
// Unary async overloads
|
||||
static_assert(std::is_member_function_pointer<
|
||||
decltype(static_cast<void (Stub::*)(
|
||||
::grpc::ClientContext *,
|
||||
const flatbuffers::grpc::Message<Monster> &,
|
||||
flatbuffers::grpc::Message<Stat> *,
|
||||
std::function<void(::grpc::Status)>)>(
|
||||
&Stub::async_Store))>::value,
|
||||
"Function-form unary async_Store missing");
|
||||
static_assert(std::is_member_function_pointer<
|
||||
decltype(static_cast<void (Stub::*)(
|
||||
::grpc::ClientContext *,
|
||||
const flatbuffers::grpc::Message<Monster> &,
|
||||
flatbuffers::grpc::Message<Stat> *,
|
||||
::grpc::ClientUnaryReactor *)>(
|
||||
&Stub::async_Store))>::value,
|
||||
"Reactor-form unary async_Store missing");
|
||||
|
||||
// Streaming reactor entry points
|
||||
static_assert(
|
||||
std::is_member_function_pointer<
|
||||
decltype(static_cast<void (Stub::*)(
|
||||
::grpc::ClientContext *,
|
||||
const flatbuffers::grpc::Message<Stat> &,
|
||||
::grpc::ClientReadReactor<flatbuffers::grpc::Message<
|
||||
Monster> > *)>(&Stub::async_Retrieve))>::value,
|
||||
"Server streaming reactor async_Retrieve missing");
|
||||
static_assert(
|
||||
std::is_member_function_pointer<
|
||||
decltype(static_cast<void (Stub::*)(
|
||||
::grpc::ClientContext *,
|
||||
flatbuffers::grpc::Message<Stat> *,
|
||||
::grpc::ClientWriteReactor<flatbuffers::grpc::Message<
|
||||
Monster> > *)>(&Stub::async_GetMaxHitPoint))>::value,
|
||||
"Client streaming reactor async_GetMaxHitPoint missing");
|
||||
static_assert(std::is_member_function_pointer<
|
||||
decltype(static_cast<void (Stub::*)(
|
||||
::grpc::ClientContext *,
|
||||
::grpc::ClientBidiReactor<
|
||||
flatbuffers::grpc::Message<Monster>,
|
||||
flatbuffers::grpc::Message<Stat> > *)>(
|
||||
&Stub::async_GetMinMaxHitPoints))>::value,
|
||||
"Bidi streaming reactor async_GetMinMaxHitPoints missing");
|
||||
#endif // FLATBUFFERS_GENERATED_GRPC_CALLBACK_API &&
|
||||
// GRPC_CALLBACK_API_NONEXPERIMENTAL
|
||||
|
||||
int main() { return 0; }
|
||||
22
grpc/tests/grpctest_callback_compile.cpp
Normal file
22
grpc/tests/grpctest_callback_compile.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "monster_test.grpc.fb.h"
|
||||
|
||||
// This test only verifies that the generated CallbackService compiles when the
|
||||
// callback API is available. It does not run any RPCs.
|
||||
|
||||
#if defined(FLATBUFFERS_GENERATED_GRPC_CALLBACK_API) && \
|
||||
defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)
|
||||
class CallbackServiceImpl
|
||||
: public MyGame::Example::MonsterStorage::CallbackService {
|
||||
public:
|
||||
// For brevity we don't override methods; user code will provide reactors.
|
||||
};
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
#if defined(FLATBUFFERS_GENERATED_GRPC_CALLBACK_API) && \
|
||||
defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)
|
||||
CallbackServiceImpl svc;
|
||||
(void)svc; // suppress unused
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user