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:
Shashank
2025-08-29 04:19:27 +05:30
committed by GitHub
parent b87d04af8c
commit deb3d93454
21 changed files with 617 additions and 51 deletions

View File

@@ -4,6 +4,10 @@ All major or breaking changes will be documented in this file, as well as any
new features that should be highlighted. Minor fixes or improvements are not
necessarily listed.
## Unreleased
* flatc: `--grpc-callback-api` flag generates C++ gRPC Callback API server `CallbackService` skeletons AND client native callback/async stubs (unary + all streaming reactor forms) (opt-in, non-breaking, issue #8596).
## [25.2.10] (February 10 2025)(https://github.com/google/flatbuffers/releases/tag/v25.2.10)
* Removed the old documentation pages. The new one is live at https://flatbuffers.dev

View File

@@ -279,6 +279,8 @@ set(FlatBuffers_GRPCTest_SRCS
tests/test_builder.cpp
grpc/tests/grpctest.cpp
grpc/tests/message_builder_test.cpp
grpc/tests/grpctest_callback_compile.cpp
grpc/tests/grpctest_callback_client_compile.cpp
)
# TODO(dbaileychess): Figure out how this would now work. I posted a question on

View File

@@ -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.

View File

@@ -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 &params) {
const std::vector<grpc::string> &headers,
const Parameters &params) {
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 &params) {
@@ -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 &params) {
@@ -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 &params) {

View File

@@ -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.

View 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++",
],
)

View 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; }

View 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;
}

View File

@@ -342,6 +342,7 @@ struct FieldDef : public Definition {
bool Deserialize(Parser &parser, const reflection::Field *field);
bool IsScalarOptional() const {
return IsScalar() && IsOptional();
}
@@ -784,6 +785,9 @@ struct IDLOptions {
bool grpc_use_system_headers;
std::string grpc_search_path;
std::vector<std::string> grpc_additional_headers;
// Generate C++ gRPC Callback API (modern async API) service code when used
// together with --grpc. This is an additive, opt-in feature.
bool grpc_callback_api;
/******************************* Python gRPC ********************************/
bool grpc_python_typed_handlers;
@@ -862,6 +866,7 @@ struct IDLOptions {
set_empty_vectors_to_null(true),
grpc_filename_suffix(".fb"),
grpc_use_system_headers(true),
grpc_callback_api(false),
grpc_python_typed_handlers(false) {}
};

View File

@@ -154,13 +154,26 @@ flatc(
include="include_test",
)
"""NOTE: The C++ gRPC golden is generated with the callback API enabled so that
the repository goldens exercise the callback client & server code paths.
If you need the legacy (non-callback) variant for comparison, invoke flatc
manually without --grpc-callback-api; we intentionally do not keep both to
minimize golden churn."""
flatc(
NO_INCL_OPTS + CPP_OPTS + ["--grpc"],
NO_INCL_OPTS + CPP_OPTS + ["--grpc", "--grpc-callback-api"],
schema="monster_test.fbs",
include="include_test",
data="monsterdata_test.json",
)
# Also generate a suffix variant exercising the callback API to keep prior
# *_generated naming convention in sync with new callback additions.
flatc(
NO_INCL_OPTS + CPP_OPTS + ["--grpc", "--grpc-callback-api", "--filename-suffix", "_generated"],
schema="monster_test.fbs",
include="include_test",
)
flatc(
RUST_OPTS,
schema="monster_test.fbs",

View File

@@ -271,6 +271,9 @@ const static FlatCOption flatc_options[] = {
{ "", "grpc-search-path", "PATH", "Prefix to any gRPC includes." },
{ "", "grpc-python-typed-handlers", "",
"The handlers will use the generated classes rather than raw bytes." },
{ "", "grpc-callback-api", "",
"Generate gRPC code using the callback (reactor) API instead of legacy "
"sync/async." },
};
auto cmp = [](FlatCOption a, FlatCOption b) { return a.long_opt < b.long_opt; };
@@ -731,6 +734,12 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc,
} else if (arg == "--no-grpc-python-typed-handlers" ||
arg == "--grpc-python-typed-handlers=false") {
opts.grpc_python_typed_handlers = false;
} else if (arg == "--grpc-callback-api" ||
arg == "--grpc-callback-api=true") {
opts.grpc_callback_api = true;
} else if (arg == "--no-grpc-callback-api" ||
arg == "--grpc-callback-api=false") {
opts.grpc_callback_api = false;
} else {
if (arg == "--proto") { opts.proto_mode = true; }

View File

@@ -372,6 +372,7 @@ bool GenerateCppGRPC(const Parser &parser, const std::string &path,
generator_parameters.service_header_extension =
".grpc" + opts.grpc_filename_suffix + ".h";
generator_parameters.grpc_search_path = opts.grpc_search_path;
generator_parameters.generate_callback_api = opts.grpc_callback_api;
std::string filename = flatbuffers::StripExtension(parser.file_being_parsed_);
if (!opts.keep_prefix) {
filename = flatbuffers::StripPath(filename);

View File

@@ -1,5 +1,5 @@
// Generated by the gRPC C++ plugin.
// If you make any local change, they will be lost.
// FlatBuffers modified generator: native gRPC callback client API enabled when --grpc-callback-api.
// source: monster_test
#include "monster_test_generated.h"
@@ -13,6 +13,9 @@
#include <grpcpp/impl/codegen/rpc_service_method.h>
#include <grpcpp/impl/codegen/service_type.h>
#include <grpcpp/impl/codegen/sync_stream.h>
#include <grpcpp/impl/codegen/callback_common.h>
#include <grpcpp/impl/codegen/server_callback_handlers.h>
#include <grpcpp/support/client_callback.h>
namespace MyGame {
namespace Example {
@@ -39,6 +42,14 @@ MonsterStorage::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& cha
return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_Store_, context, request, response);
}
void MonsterStorage::Stub::async_Store(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, flatbuffers::grpc::Message<Stat>* response, std::function<void(::grpc::Status)> on_done) {
::grpc::internal::CallbackUnaryCall(channel_.get(), rpcmethod_Store_, context, &request, response, std::move(on_done));
}
void MonsterStorage::Stub::async_Store(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, flatbuffers::grpc::Message<Stat>* response, ::grpc::ClientUnaryReactor* reactor) {
::grpc::internal::ClientCallbackUnaryFactory::Create(channel_.get(), rpcmethod_Store_, context, &request, response, reactor);
}
::grpc::ClientAsyncResponseReader< flatbuffers::grpc::Message<Stat>>* MonsterStorage::Stub::AsyncStoreRaw(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, ::grpc::CompletionQueue* cq) {
return ::grpc::internal::ClientAsyncResponseReaderFactory< flatbuffers::grpc::Message<Stat>>::Create(channel_.get(), cq, rpcmethod_Store_, context, request, true);
}
@@ -51,6 +62,10 @@ MonsterStorage::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& cha
return ::grpc::internal::ClientReaderFactory< flatbuffers::grpc::Message<Monster>>::Create(channel_.get(), rpcmethod_Retrieve_, context, request);
}
void MonsterStorage::Stub::async_Retrieve(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Stat>& request, ::grpc::ClientReadReactor< flatbuffers::grpc::Message<Monster> >* reactor) {
::grpc::internal::ClientCallbackReaderFactory< flatbuffers::grpc::Message<Monster> >::Create(channel_.get(), rpcmethod_Retrieve_, context, &request, reactor);
}
::grpc::ClientAsyncReader< flatbuffers::grpc::Message<Monster>>* MonsterStorage::Stub::AsyncRetrieveRaw(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Stat>& request, ::grpc::CompletionQueue* cq, void* tag) {
return ::grpc::internal::ClientAsyncReaderFactory< flatbuffers::grpc::Message<Monster>>::Create(channel_.get(), cq, rpcmethod_Retrieve_, context, request, true, tag);
}
@@ -63,6 +78,10 @@ MonsterStorage::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& cha
return ::grpc::internal::ClientWriterFactory< flatbuffers::grpc::Message<Monster>>::Create(channel_.get(), rpcmethod_GetMaxHitPoint_, context, response);
}
void MonsterStorage::Stub::async_GetMaxHitPoint(::grpc::ClientContext* context, flatbuffers::grpc::Message<Stat>* response, ::grpc::ClientWriteReactor< flatbuffers::grpc::Message<Monster> >* reactor) {
::grpc::internal::ClientCallbackWriterFactory< flatbuffers::grpc::Message<Monster> >::Create(channel_.get(), rpcmethod_GetMaxHitPoint_, context, response, reactor);
}
::grpc::ClientAsyncWriter< flatbuffers::grpc::Message<Monster>>* MonsterStorage::Stub::AsyncGetMaxHitPointRaw(::grpc::ClientContext* context, flatbuffers::grpc::Message<Stat>* response, ::grpc::CompletionQueue* cq, void* tag) {
return ::grpc::internal::ClientAsyncWriterFactory< flatbuffers::grpc::Message<Monster>>::Create(channel_.get(), cq, rpcmethod_GetMaxHitPoint_, context, response, true, tag);
}
@@ -75,6 +94,10 @@ MonsterStorage::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& cha
return ::grpc::internal::ClientReaderWriterFactory< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>::Create(channel_.get(), rpcmethod_GetMinMaxHitPoints_, context);
}
void MonsterStorage::Stub::async_GetMinMaxHitPoints(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat> >* reactor) {
::grpc::internal::ClientCallbackReaderWriterFactory< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat> >::Create(channel_.get(), rpcmethod_GetMinMaxHitPoints_, context, reactor);
}
::grpc::ClientAsyncReaderWriter< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>* MonsterStorage::Stub::AsyncGetMinMaxHitPointsRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
return ::grpc::internal::ClientAsyncReaderWriterFactory< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>::Create(channel_.get(), cq, rpcmethod_GetMinMaxHitPoints_, context, true, tag);
}
@@ -125,6 +148,57 @@ MonsterStorage::Service::~Service() {
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
#if defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)
MonsterStorage::CallbackService::CallbackService() {
AddMethod(new ::grpc::internal::RpcServiceMethod(
MonsterStorage_method_names[0],
::grpc::internal::RpcMethod::NORMAL_RPC,
new ::grpc::internal::CallbackUnaryHandler<flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>(
[this](::grpc::CallbackServerContext* ctx, const flatbuffers::grpc::Message<Monster>* req, flatbuffers::grpc::Message<Stat>* resp) {
return this->Store(ctx, req, resp);
})));
AddMethod(new ::grpc::internal::RpcServiceMethod(
MonsterStorage_method_names[1],
::grpc::internal::RpcMethod::SERVER_STREAMING,
new ::grpc::internal::CallbackServerStreamingHandler<flatbuffers::grpc::Message<Stat>, flatbuffers::grpc::Message<Monster>>(
[this](::grpc::CallbackServerContext* ctx, const flatbuffers::grpc::Message<Stat>* req) {
return this->Retrieve(ctx, req);
})));
AddMethod(new ::grpc::internal::RpcServiceMethod(
MonsterStorage_method_names[2],
::grpc::internal::RpcMethod::CLIENT_STREAMING,
new ::grpc::internal::CallbackClientStreamingHandler<flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>(
[this](::grpc::CallbackServerContext* ctx, flatbuffers::grpc::Message<Stat>* resp) {
return this->GetMaxHitPoint(ctx, resp);
})));
AddMethod(new ::grpc::internal::RpcServiceMethod(
MonsterStorage_method_names[3],
::grpc::internal::RpcMethod::BIDI_STREAMING,
new ::grpc::internal::CallbackBidiHandler<flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>(
[this](::grpc::CallbackServerContext* ctx) {
return this->GetMinMaxHitPoints(ctx);
})));
}
MonsterStorage::CallbackService::~CallbackService() {}
::grpc::ServerUnaryReactor* MonsterStorage::CallbackService::Store(::grpc::CallbackServerContext* /*context*/, const flatbuffers::grpc::Message<Monster>* /*request*/, flatbuffers::grpc::Message<Stat>* /*response*/) {
return nullptr; // user must override
}
::grpc::ServerWriteReactor<flatbuffers::grpc::Message<Monster>>* MonsterStorage::CallbackService::Retrieve(::grpc::CallbackServerContext* /*context*/, const flatbuffers::grpc::Message<Stat>* /*request*/) {
return nullptr; // user must override
}
::grpc::ServerReadReactor<flatbuffers::grpc::Message<Monster>>* MonsterStorage::CallbackService::GetMaxHitPoint(::grpc::CallbackServerContext* /*context*/, flatbuffers::grpc::Message<Stat>* /*response*/) {
return nullptr; // user must override
}
::grpc::ServerBidiReactor<flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>* MonsterStorage::CallbackService::GetMinMaxHitPoints(::grpc::CallbackServerContext* /*context*/) {
return nullptr; // user must override
}
#endif // GRPC_CALLBACK_API_NONEXPERIMENTAL
} // namespace MyGame
} // namespace Example

View File

@@ -16,6 +16,9 @@
#include <grpcpp/impl/codegen/status.h>
#include <grpcpp/impl/codegen/stub_options.h>
#include <grpcpp/impl/codegen/sync_stream.h>
#include <grpcpp/impl/codegen/callback_common.h>
#include <grpcpp/impl/codegen/server_callback_handlers.h>
#include <grpcpp/support/client_callback.h>
namespace grpc {
class CompletionQueue;
@@ -81,11 +84,16 @@ class MonsterStorage final {
virtual ::grpc::ClientReaderWriterInterface< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>* GetMinMaxHitPointsRaw(::grpc::ClientContext* context) = 0;
virtual ::grpc::ClientAsyncReaderWriterInterface< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>* AsyncGetMinMaxHitPointsRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) = 0;
virtual ::grpc::ClientAsyncReaderWriterInterface< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>* PrepareAsyncGetMinMaxHitPointsRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) = 0;
class CallbackService;
};
class Stub final : public StubInterface {
public:
Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);
::grpc::Status Store(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, flatbuffers::grpc::Message<Stat>* response) override;
// Callback unary (function form). Request/response must outlive callback.
void async_Store(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, flatbuffers::grpc::Message<Stat>* response, std::function<void(::grpc::Status)> on_done);
// Callback unary (reactor form).
void async_Store(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, flatbuffers::grpc::Message<Stat>* response, ::grpc::ClientUnaryReactor* reactor);
std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::grpc::Message<Stat>>> AsyncStore(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Monster>& request, ::grpc::CompletionQueue* cq) {
return std::unique_ptr< ::grpc::ClientAsyncResponseReader< flatbuffers::grpc::Message<Stat>>>(AsyncStoreRaw(context, request, cq));
}
@@ -95,6 +103,8 @@ class MonsterStorage final {
std::unique_ptr< ::grpc::ClientReader< flatbuffers::grpc::Message<Monster>>> Retrieve(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Stat>& request) {
return std::unique_ptr< ::grpc::ClientReader< flatbuffers::grpc::Message<Monster>>>(RetrieveRaw(context, request));
}
// Server streaming callback reactor entry.
void async_Retrieve(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Stat>& request, ::grpc::ClientReadReactor< flatbuffers::grpc::Message<Monster> >* reactor);
std::unique_ptr< ::grpc::ClientAsyncReader< flatbuffers::grpc::Message<Monster>>> AsyncRetrieve(::grpc::ClientContext* context, const flatbuffers::grpc::Message<Stat>& request, ::grpc::CompletionQueue* cq, void* tag) {
return std::unique_ptr< ::grpc::ClientAsyncReader< flatbuffers::grpc::Message<Monster>>>(AsyncRetrieveRaw(context, request, cq, tag));
}
@@ -104,6 +114,8 @@ class MonsterStorage final {
std::unique_ptr< ::grpc::ClientWriter< flatbuffers::grpc::Message<Monster>>> GetMaxHitPoint(::grpc::ClientContext* context, flatbuffers::grpc::Message<Stat>* response) {
return std::unique_ptr< ::grpc::ClientWriter< flatbuffers::grpc::Message<Monster>>>(GetMaxHitPointRaw(context, response));
}
// Client streaming callback reactor entry.
void async_GetMaxHitPoint(::grpc::ClientContext* context, flatbuffers::grpc::Message<Stat>* response, ::grpc::ClientWriteReactor< flatbuffers::grpc::Message<Monster> >* reactor);
std::unique_ptr< ::grpc::ClientAsyncWriter< flatbuffers::grpc::Message<Monster>>> AsyncGetMaxHitPoint(::grpc::ClientContext* context, flatbuffers::grpc::Message<Stat>* response, ::grpc::CompletionQueue* cq, void* tag) {
return std::unique_ptr< ::grpc::ClientAsyncWriter< flatbuffers::grpc::Message<Monster>>>(AsyncGetMaxHitPointRaw(context, response, cq, tag));
}
@@ -113,6 +125,8 @@ class MonsterStorage final {
std::unique_ptr< ::grpc::ClientReaderWriter< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>> GetMinMaxHitPoints(::grpc::ClientContext* context) {
return std::unique_ptr< ::grpc::ClientReaderWriter< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>>(GetMinMaxHitPointsRaw(context));
}
// Bidirectional streaming callback reactor entry.
void async_GetMinMaxHitPoints(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat> >* reactor);
std::unique_ptr< ::grpc::ClientAsyncReaderWriter< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>> AsyncGetMinMaxHitPoints(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
return std::unique_ptr< ::grpc::ClientAsyncReaderWriter< flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>>(AsyncGetMinMaxHitPointsRaw(context, cq, tag));
}
@@ -343,6 +357,23 @@ class MonsterStorage final {
typedef WithStreamedUnaryMethod_Store< WithSplitStreamingMethod_Retrieve< Service > > StreamedService;
};
#if defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)
class MonsterStorage::CallbackService : public ::grpc::Service {
public:
CallbackService();
virtual ~CallbackService();
virtual ::grpc::ServerUnaryReactor* Store(::grpc::CallbackServerContext* context, const flatbuffers::grpc::Message<Monster>* request, flatbuffers::grpc::Message<Stat>* response);
virtual ::grpc::ServerWriteReactor<flatbuffers::grpc::Message<Monster>>* Retrieve(::grpc::CallbackServerContext* context, const flatbuffers::grpc::Message<Stat>* request);
virtual ::grpc::ServerReadReactor<flatbuffers::grpc::Message<Monster>>* GetMaxHitPoint(::grpc::CallbackServerContext* context, flatbuffers::grpc::Message<Stat>* response);
virtual ::grpc::ServerBidiReactor<flatbuffers::grpc::Message<Monster>, flatbuffers::grpc::Message<Stat>>* GetMinMaxHitPoints(::grpc::CallbackServerContext* context);
};
#else
// Callback API requested but not available in this gRPC version.
#endif // GRPC_CALLBACK_API_NONEXPERIMENTAL
// FlatBuffers: Callback API code generated.
#define FLATBUFFERS_GENERATED_GRPC_CALLBACK_API 1
} // namespace Example
} // namespace MyGame

View File

@@ -0,0 +1,2 @@
// Callback variant source references primary generated implementation.
#include "monster_test_callback.grpc.fb.h"

View File

@@ -0,0 +1,4 @@
// Callback variant copy of monster_test.grpc.fb.h generated with
// --grpc-callback-api (kept separate as a golden for callback API feature
// evolution)
#include "monster_test.grpc.fb.h"

View File

@@ -1,8 +1,9 @@
// Generated by the gRPC C++ plugin.
// If you make any local change, they will be lost.
// FlatBuffers modified generator: native gRPC callback client API enabled when --grpc-callback-api.
// source: monster_test
#include "monster_test_generated.grpc.fb.h"
#include "monster_test_generated.h"
#include "monster_test.grpc.fb.h"
#include <grpcpp/impl/codegen/async_stream.h>
#include <grpcpp/impl/codegen/async_unary_call.h>

View File

@@ -201,7 +201,7 @@ class MonsterStorage final {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
::grpc::Status GetMaxHitPoint(::grpc::ServerContext* /*context*/, ::grpc::ServerReader< flatbuffers::grpc::Message<Monster>>* /*reader*/, flatbuffers::grpc::Message<Stat>* response) final override {
::grpc::Status GetMaxHitPoint(::grpc::ServerContext* /*context*/, ::grpc::ServerReader< flatbuffers::grpc::Message<Monster>>* /*reader*/, flatbuffers::grpc::Message<Stat>* /*response*/) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}

View File

@@ -1,5 +1,5 @@
// Generated by the gRPC C++ plugin.
// If you make any local change, they will be lost.
// FlatBuffers modified generator: native gRPC callback client API enabled when --grpc-callback-api.
// source: monster_test
#include "monster_test_generated.hpp"

View File

@@ -1,5 +1,5 @@
// Generated by the gRPC C++ plugin.
// If you make any local change, they will be lost.
// FlatBuffers modified generator: native gRPC callback client API enabled when --grpc-callback-api.
// source: monster_test
#include "monster_test_suffix.h"

View File

@@ -1,5 +1,5 @@
// Generated by the gRPC C++ plugin.
// If you make any local change, they will be lost.
// FlatBuffers modified generator: native gRPC callback client API enabled when --grpc-callback-api.
// source: monster_test
#include "monster_test_suffix.hpp"