I was looking for a serialization option that was flexible and performant and flatbuffers caught my eye.
Overview
For C++, Flatbuffers is a header-only library
It includes source for a compiler to convert the schema (fbs files) into c++ code.
More Details
FlatBuffers is an efficient cross-platform serialization library developed by Google, originally designed for game development and other performance-critical applications. It offers some unique features that distinguish it from other serialization libraries like Protocol Buffers (Protobuf) or JSON.
Memory Efficiency and Speed: FlatBuffers are designed to be memory efficient and fast. They allow you to access serialized data without parsing or unpacking it first, which means there is no overhead of parsing and allocating new memory. This is particularly beneficial for performance-critical applications like games or real-time systems.
Zero-Copy Deserialization: One of the key features of FlatBuffers is that it enables "zero-copy" deserialization. This means that data can be accessed directly from the buffer without any additional parsing step or memory allocation, significantly reducing the CPU load.
Language Support: FlatBuffers support multiple programming languages, including C++, C#, C, Go, Java, JavaScript, PHP, Python, and TypeScript. This makes it versatile for use in various types of applications and systems.
Backward and Forward Compatibility: Like Protocol Buffers, FlatBuffers are designed to be backward and forward compatible. This means that you can add new fields to your data structures without breaking existing code that uses older versions of those structures.
Flexible Schema System: FlatBuffers use a schema to define the data structures. This schema is written in a language called FlatBuffers Interface Definition Language (FIDL). The schema allows for strong type checking, but it's also flexible enough to accommodate changes for backward and forward compatibility.
Binary Format: The serialized data is in a compact binary format, which is smaller than equivalent JSON or XML representations. This makes it suitable for network communication and storage where bandwidth or storage space is limited.
Use Cases: FlatBuffers are commonly used in gaming, mobile applications, and real-time systems where performance is critical. They are also used for communication between different components in a distributed system or for storing structured data.
Tooling: FlatBuffers come with a compiler tool that takes a schema definition file (FIDL) and generates the necessary code in supported languages. This simplifies the process of working with data structures defined in the schema.
In summary, FlatBuffers are a powerful tool for efficient serialization and deserialization, particularly in contexts where performance and resource utilization are critical factors. Their design allows for direct, zero-copy access to serialized data, making them an attractive choice for high-performance applications.
Performance
Their benchmarks indicate significant performance over protobufs among others.
https://flatbuffers.dev/flatbuffers_benchmarks.html
I found an independent evaluation which seemed to indicate the same.
https://codeburst.io/json-vs-protocol-buffers-vs-flatbuffers-a4247f8bda6f
Building the compiler
I chose to build with Visual Studio 2022 since I had it readily available.
clone or download the repo, extract and open command prompt to the source root.
cmake -G "Visual Studio 17"
side-note: I wasn't sure what number VS2022 was; if you run
cmake --help
, it lets you know which one it detects.open "FlatBuffers.sln", select "Release" and build all via Visual Studio.
Examples
Simple Example 1
compiling the schema: flatc -c log_schema.fbs
log_schema.fbs
// Example FlatBuffers schema for controlling a logging system.
namespace LoggingSystem;
// Enum to represent different log control commands.
enum LogCommand: byte {
Start,
Stop,
Pause
}
// Request message to control the logging.
table LogControlRequest {
command:LogCommand;
}
// Status of the logging system to be sent as a response.
enum LogStatus: byte {
Running,
Stopped,
Paused,
Error // In case of an error in processing the request.
}
// Response message with the status of the logging system.
table LogControlResponse {
status:LogStatus;
message:string; // Additional information or error message.
}
// Root type can be a union of request and response for simplicity.
union LogMessage {
LogControlRequest,
LogControlResponse
}
table LogMessageHolder
{
the_message:LogMessage;
}
root_type LogMessageHolder;
log_schema_generated.h
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_LOGSCHEMA_LOGGINGSYSTEM_H_
#define FLATBUFFERS_GENERATED_LOGSCHEMA_LOGGINGSYSTEM_H_
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 23 &&
FLATBUFFERS_VERSION_MINOR == 5 &&
FLATBUFFERS_VERSION_REVISION == 26,
"Non-compatible flatbuffers version included");
namespace LoggingSystem {
struct LogControlRequest;
struct LogControlRequestBuilder;
struct LogControlResponse;
struct LogControlResponseBuilder;
struct LogMessageHolder;
struct LogMessageHolderBuilder;
enum LogCommand : int8_t {
LogCommand_Start = 0,
LogCommand_Stop = 1,
LogCommand_Pause = 2,
LogCommand_MIN = LogCommand_Start,
LogCommand_MAX = LogCommand_Pause
};
inline const LogCommand (&EnumValuesLogCommand())[3] {
static const LogCommand values[] = {
LogCommand_Start,
LogCommand_Stop,
LogCommand_Pause
};
return values;
}
inline const char * const *EnumNamesLogCommand() {
static const char * const names[4] = {
"Start",
"Stop",
"Pause",
nullptr
};
return names;
}
inline const char *EnumNameLogCommand(LogCommand e) {
if (::flatbuffers::IsOutRange(e, LogCommand_Start, LogCommand_Pause)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesLogCommand()[index];
}
enum LogStatus : int8_t {
LogStatus_Running = 0,
LogStatus_Stopped = 1,
LogStatus_Paused = 2,
LogStatus_Error = 3,
LogStatus_MIN = LogStatus_Running,
LogStatus_MAX = LogStatus_Error
};
inline const LogStatus (&EnumValuesLogStatus())[4] {
static const LogStatus values[] = {
LogStatus_Running,
LogStatus_Stopped,
LogStatus_Paused,
LogStatus_Error
};
return values;
}
inline const char * const *EnumNamesLogStatus() {
static const char * const names[5] = {
"Running",
"Stopped",
"Paused",
"Error",
nullptr
};
return names;
}
inline const char *EnumNameLogStatus(LogStatus e) {
if (::flatbuffers::IsOutRange(e, LogStatus_Running, LogStatus_Error)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesLogStatus()[index];
}
enum LogMessage : uint8_t {
LogMessage_NONE = 0,
LogMessage_LogControlRequest = 1,
LogMessage_LogControlResponse = 2,
LogMessage_MIN = LogMessage_NONE,
LogMessage_MAX = LogMessage_LogControlResponse
};
inline const LogMessage (&EnumValuesLogMessage())[3] {
static const LogMessage values[] = {
LogMessage_NONE,
LogMessage_LogControlRequest,
LogMessage_LogControlResponse
};
return values;
}
inline const char * const *EnumNamesLogMessage() {
static const char * const names[4] = {
"NONE",
"LogControlRequest",
"LogControlResponse",
nullptr
};
return names;
}
inline const char *EnumNameLogMessage(LogMessage e) {
if (::flatbuffers::IsOutRange(e, LogMessage_NONE, LogMessage_LogControlResponse)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesLogMessage()[index];
}
template<typename T> struct LogMessageTraits {
static const LogMessage enum_value = LogMessage_NONE;
};
template<> struct LogMessageTraits<LoggingSystem::LogControlRequest> {
static const LogMessage enum_value = LogMessage_LogControlRequest;
};
template<> struct LogMessageTraits<LoggingSystem::LogControlResponse> {
static const LogMessage enum_value = LogMessage_LogControlResponse;
};
bool VerifyLogMessage(::flatbuffers::Verifier &verifier, const void *obj, LogMessage type);
bool VerifyLogMessageVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<uint8_t> *types);
struct LogControlRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef LogControlRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_COMMAND = 4
};
LoggingSystem::LogCommand command() const {
return static_cast<LoggingSystem::LogCommand>(GetField<int8_t>(VT_COMMAND, 0));
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int8_t>(verifier, VT_COMMAND, 1) &&
verifier.EndTable();
}
};
struct LogControlRequestBuilder {
typedef LogControlRequest Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_command(LoggingSystem::LogCommand command) {
fbb_.AddElement<int8_t>(LogControlRequest::VT_COMMAND, static_cast<int8_t>(command), 0);
}
explicit LogControlRequestBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<LogControlRequest> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<LogControlRequest>(end);
return o;
}
};
inline ::flatbuffers::Offset<LogControlRequest> CreateLogControlRequest(
::flatbuffers::FlatBufferBuilder &_fbb,
LoggingSystem::LogCommand command = LoggingSystem::LogCommand_Start) {
LogControlRequestBuilder builder_(_fbb);
builder_.add_command(command);
return builder_.Finish();
}
struct LogControlResponse FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef LogControlResponseBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_STATUS = 4,
VT_MESSAGE = 6
};
LoggingSystem::LogStatus status() const {
return static_cast<LoggingSystem::LogStatus>(GetField<int8_t>(VT_STATUS, 0));
}
const ::flatbuffers::String *message() const {
return GetPointer<const ::flatbuffers::String *>(VT_MESSAGE);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int8_t>(verifier, VT_STATUS, 1) &&
VerifyOffset(verifier, VT_MESSAGE) &&
verifier.VerifyString(message()) &&
verifier.EndTable();
}
};
struct LogControlResponseBuilder {
typedef LogControlResponse Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_status(LoggingSystem::LogStatus status) {
fbb_.AddElement<int8_t>(LogControlResponse::VT_STATUS, static_cast<int8_t>(status), 0);
}
void add_message(::flatbuffers::Offset<::flatbuffers::String> message) {
fbb_.AddOffset(LogControlResponse::VT_MESSAGE, message);
}
explicit LogControlResponseBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<LogControlResponse> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<LogControlResponse>(end);
return o;
}
};
inline ::flatbuffers::Offset<LogControlResponse> CreateLogControlResponse(
::flatbuffers::FlatBufferBuilder &_fbb,
LoggingSystem::LogStatus status = LoggingSystem::LogStatus_Running,
::flatbuffers::Offset<::flatbuffers::String> message = 0) {
LogControlResponseBuilder builder_(_fbb);
builder_.add_message(message);
builder_.add_status(status);
return builder_.Finish();
}
inline ::flatbuffers::Offset<LogControlResponse> CreateLogControlResponseDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
LoggingSystem::LogStatus status = LoggingSystem::LogStatus_Running,
const char *message = nullptr) {
auto message__ = message ? _fbb.CreateString(message) : 0;
return LoggingSystem::CreateLogControlResponse(
_fbb,
status,
message__);
}
struct LogMessageHolder FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef LogMessageHolderBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_THE_MESSAGE_TYPE = 4,
VT_THE_MESSAGE = 6
};
LoggingSystem::LogMessage the_message_type() const {
return static_cast<LoggingSystem::LogMessage>(GetField<uint8_t>(VT_THE_MESSAGE_TYPE, 0));
}
const void *the_message() const {
return GetPointer<const void *>(VT_THE_MESSAGE);
}
template<typename T> const T *the_message_as() const;
const LoggingSystem::LogControlRequest *the_message_as_LogControlRequest() const {
return the_message_type() == LoggingSystem::LogMessage_LogControlRequest ? static_cast<const LoggingSystem::LogControlRequest *>(the_message()) : nullptr;
}
const LoggingSystem::LogControlResponse *the_message_as_LogControlResponse() const {
return the_message_type() == LoggingSystem::LogMessage_LogControlResponse ? static_cast<const LoggingSystem::LogControlResponse *>(the_message()) : nullptr;
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_THE_MESSAGE_TYPE, 1) &&
VerifyOffset(verifier, VT_THE_MESSAGE) &&
VerifyLogMessage(verifier, the_message(), the_message_type()) &&
verifier.EndTable();
}
};
template<> inline const LoggingSystem::LogControlRequest *LogMessageHolder::the_message_as<LoggingSystem::LogControlRequest>() const {
return the_message_as_LogControlRequest();
}
template<> inline const LoggingSystem::LogControlResponse *LogMessageHolder::the_message_as<LoggingSystem::LogControlResponse>() const {
return the_message_as_LogControlResponse();
}
struct LogMessageHolderBuilder {
typedef LogMessageHolder Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_the_message_type(LoggingSystem::LogMessage the_message_type) {
fbb_.AddElement<uint8_t>(LogMessageHolder::VT_THE_MESSAGE_TYPE, static_cast<uint8_t>(the_message_type), 0);
}
void add_the_message(::flatbuffers::Offset<void> the_message) {
fbb_.AddOffset(LogMessageHolder::VT_THE_MESSAGE, the_message);
}
explicit LogMessageHolderBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<LogMessageHolder> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<LogMessageHolder>(end);
return o;
}
};
inline ::flatbuffers::Offset<LogMessageHolder> CreateLogMessageHolder(
::flatbuffers::FlatBufferBuilder &_fbb,
LoggingSystem::LogMessage the_message_type = LoggingSystem::LogMessage_NONE,
::flatbuffers::Offset<void> the_message = 0) {
LogMessageHolderBuilder builder_(_fbb);
builder_.add_the_message(the_message);
builder_.add_the_message_type(the_message_type);
return builder_.Finish();
}
inline bool VerifyLogMessage(::flatbuffers::Verifier &verifier, const void *obj, LogMessage type) {
switch (type) {
case LogMessage_NONE: {
return true;
}
case LogMessage_LogControlRequest: {
auto ptr = reinterpret_cast<const LoggingSystem::LogControlRequest *>(obj);
return verifier.VerifyTable(ptr);
}
case LogMessage_LogControlResponse: {
auto ptr = reinterpret_cast<const LoggingSystem::LogControlResponse *>(obj);
return verifier.VerifyTable(ptr);
}
default: return true;
}
}
inline bool VerifyLogMessageVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<uint8_t> *types) {
if (!values || !types) return !values && !types;
if (values->size() != types->size()) return false;
for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
if (!VerifyLogMessage(
verifier, values->Get(i), types->GetEnum<LogMessage>(i))) {
return false;
}
}
return true;
}
inline const LoggingSystem::LogMessageHolder *GetLogMessageHolder(const void *buf) {
return ::flatbuffers::GetRoot<LoggingSystem::LogMessageHolder>(buf);
}
inline const LoggingSystem::LogMessageHolder *GetSizePrefixedLogMessageHolder(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<LoggingSystem::LogMessageHolder>(buf);
}
inline bool VerifyLogMessageHolderBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<LoggingSystem::LogMessageHolder>(nullptr);
}
inline bool VerifySizePrefixedLogMessageHolderBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<LoggingSystem::LogMessageHolder>(nullptr);
}
inline void FinishLogMessageHolderBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<LoggingSystem::LogMessageHolder> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedLogMessageHolderBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<LoggingSystem::LogMessageHolder> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace LoggingSystem
#endif // FLATBUFFERS_GENERATED_LOGSCHEMA_LOGGINGSYSTEM_H_
Simple Example 2
Let's say we have multiple requests specific to a certain topic and we want a server to read any of the requests and respond accordingly.
compiling the schema: flatc -c multi_request.fbs
multi_request.fbs
namespace MyService.Topic;
enum RequestType : byte { RequestA, RequestB, RequestC }
table RequestA {
// fields specific to RequestA
command:string;
}
table ResponseA {
// fields specific to ResponseA
value:string;
}
table RequestB {
// fields specific to RequestB
command:string;
}
table ResponseB {
// fields specific to ResponseB
value:string;
}
table RequestC {
// fields specific to RequestB
command:string;
}
table ResponseC {
// fields specific to ResponseB
value:string;
}
// Define a union of all request types
union AnyRequest { RequestA, RequestB, RequestC }
union AnyResponse { ResponseA, ResponseB, ResponseC }
table Message {
message_type: RequestType;
request: AnyRequest;
response: AnyResponse;
}
root_type Message;
multi_request_generated.h
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_MULTIREQUEST_MYSERVICE_TOPIC_H_
#define FLATBUFFERS_GENERATED_MULTIREQUEST_MYSERVICE_TOPIC_H_
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 23 &&
FLATBUFFERS_VERSION_MINOR == 5 &&
FLATBUFFERS_VERSION_REVISION == 26,
"Non-compatible flatbuffers version included");
namespace MyService {
namespace Topic {
struct RequestA;
struct RequestABuilder;
struct ResponseA;
struct ResponseABuilder;
struct RequestB;
struct RequestBBuilder;
struct ResponseB;
struct ResponseBBuilder;
struct RequestC;
struct RequestCBuilder;
struct ResponseC;
struct ResponseCBuilder;
struct Message;
struct MessageBuilder;
enum RequestType : int8_t {
RequestType_RequestA = 0,
RequestType_RequestB = 1,
RequestType_RequestC = 2,
RequestType_MIN = RequestType_RequestA,
RequestType_MAX = RequestType_RequestC
};
inline const RequestType (&EnumValuesRequestType())[3] {
static const RequestType values[] = {
RequestType_RequestA,
RequestType_RequestB,
RequestType_RequestC
};
return values;
}
inline const char * const *EnumNamesRequestType() {
static const char * const names[4] = {
"RequestA",
"RequestB",
"RequestC",
nullptr
};
return names;
}
inline const char *EnumNameRequestType(RequestType e) {
if (::flatbuffers::IsOutRange(e, RequestType_RequestA, RequestType_RequestC)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesRequestType()[index];
}
enum AnyRequest : uint8_t {
AnyRequest_NONE = 0,
AnyRequest_RequestA = 1,
AnyRequest_RequestB = 2,
AnyRequest_RequestC = 3,
AnyRequest_MIN = AnyRequest_NONE,
AnyRequest_MAX = AnyRequest_RequestC
};
inline const AnyRequest (&EnumValuesAnyRequest())[4] {
static const AnyRequest values[] = {
AnyRequest_NONE,
AnyRequest_RequestA,
AnyRequest_RequestB,
AnyRequest_RequestC
};
return values;
}
inline const char * const *EnumNamesAnyRequest() {
static const char * const names[5] = {
"NONE",
"RequestA",
"RequestB",
"RequestC",
nullptr
};
return names;
}
inline const char *EnumNameAnyRequest(AnyRequest e) {
if (::flatbuffers::IsOutRange(e, AnyRequest_NONE, AnyRequest_RequestC)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesAnyRequest()[index];
}
template<typename T> struct AnyRequestTraits {
static const AnyRequest enum_value = AnyRequest_NONE;
};
template<> struct AnyRequestTraits<MyService::Topic::RequestA> {
static const AnyRequest enum_value = AnyRequest_RequestA;
};
template<> struct AnyRequestTraits<MyService::Topic::RequestB> {
static const AnyRequest enum_value = AnyRequest_RequestB;
};
template<> struct AnyRequestTraits<MyService::Topic::RequestC> {
static const AnyRequest enum_value = AnyRequest_RequestC;
};
bool VerifyAnyRequest(::flatbuffers::Verifier &verifier, const void *obj, AnyRequest type);
bool VerifyAnyRequestVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<uint8_t> *types);
enum AnyResponse : uint8_t {
AnyResponse_NONE = 0,
AnyResponse_ResponseA = 1,
AnyResponse_ResponseB = 2,
AnyResponse_ResponseC = 3,
AnyResponse_MIN = AnyResponse_NONE,
AnyResponse_MAX = AnyResponse_ResponseC
};
inline const AnyResponse (&EnumValuesAnyResponse())[4] {
static const AnyResponse values[] = {
AnyResponse_NONE,
AnyResponse_ResponseA,
AnyResponse_ResponseB,
AnyResponse_ResponseC
};
return values;
}
inline const char * const *EnumNamesAnyResponse() {
static const char * const names[5] = {
"NONE",
"ResponseA",
"ResponseB",
"ResponseC",
nullptr
};
return names;
}
inline const char *EnumNameAnyResponse(AnyResponse e) {
if (::flatbuffers::IsOutRange(e, AnyResponse_NONE, AnyResponse_ResponseC)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesAnyResponse()[index];
}
template<typename T> struct AnyResponseTraits {
static const AnyResponse enum_value = AnyResponse_NONE;
};
template<> struct AnyResponseTraits<MyService::Topic::ResponseA> {
static const AnyResponse enum_value = AnyResponse_ResponseA;
};
template<> struct AnyResponseTraits<MyService::Topic::ResponseB> {
static const AnyResponse enum_value = AnyResponse_ResponseB;
};
template<> struct AnyResponseTraits<MyService::Topic::ResponseC> {
static const AnyResponse enum_value = AnyResponse_ResponseC;
};
bool VerifyAnyResponse(::flatbuffers::Verifier &verifier, const void *obj, AnyResponse type);
bool VerifyAnyResponseVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<uint8_t> *types);
struct RequestA FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef RequestABuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_COMMAND = 4
};
const ::flatbuffers::String *command() const {
return GetPointer<const ::flatbuffers::String *>(VT_COMMAND);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_COMMAND) &&
verifier.VerifyString(command()) &&
verifier.EndTable();
}
};
struct RequestABuilder {
typedef RequestA Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_command(::flatbuffers::Offset<::flatbuffers::String> command) {
fbb_.AddOffset(RequestA::VT_COMMAND, command);
}
explicit RequestABuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<RequestA> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<RequestA>(end);
return o;
}
};
inline ::flatbuffers::Offset<RequestA> CreateRequestA(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> command = 0) {
RequestABuilder builder_(_fbb);
builder_.add_command(command);
return builder_.Finish();
}
inline ::flatbuffers::Offset<RequestA> CreateRequestADirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *command = nullptr) {
auto command__ = command ? _fbb.CreateString(command) : 0;
return MyService::Topic::CreateRequestA(
_fbb,
command__);
}
struct ResponseA FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef ResponseABuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_VALUE = 4
};
const ::flatbuffers::String *value() const {
return GetPointer<const ::flatbuffers::String *>(VT_VALUE);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_VALUE) &&
verifier.VerifyString(value()) &&
verifier.EndTable();
}
};
struct ResponseABuilder {
typedef ResponseA Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_value(::flatbuffers::Offset<::flatbuffers::String> value) {
fbb_.AddOffset(ResponseA::VT_VALUE, value);
}
explicit ResponseABuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<ResponseA> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<ResponseA>(end);
return o;
}
};
inline ::flatbuffers::Offset<ResponseA> CreateResponseA(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> value = 0) {
ResponseABuilder builder_(_fbb);
builder_.add_value(value);
return builder_.Finish();
}
inline ::flatbuffers::Offset<ResponseA> CreateResponseADirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *value = nullptr) {
auto value__ = value ? _fbb.CreateString(value) : 0;
return MyService::Topic::CreateResponseA(
_fbb,
value__);
}
struct RequestB FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef RequestBBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_COMMAND = 4
};
const ::flatbuffers::String *command() const {
return GetPointer<const ::flatbuffers::String *>(VT_COMMAND);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_COMMAND) &&
verifier.VerifyString(command()) &&
verifier.EndTable();
}
};
struct RequestBBuilder {
typedef RequestB Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_command(::flatbuffers::Offset<::flatbuffers::String> command) {
fbb_.AddOffset(RequestB::VT_COMMAND, command);
}
explicit RequestBBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<RequestB> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<RequestB>(end);
return o;
}
};
inline ::flatbuffers::Offset<RequestB> CreateRequestB(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> command = 0) {
RequestBBuilder builder_(_fbb);
builder_.add_command(command);
return builder_.Finish();
}
inline ::flatbuffers::Offset<RequestB> CreateRequestBDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *command = nullptr) {
auto command__ = command ? _fbb.CreateString(command) : 0;
return MyService::Topic::CreateRequestB(
_fbb,
command__);
}
struct ResponseB FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef ResponseBBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_VALUE = 4
};
const ::flatbuffers::String *value() const {
return GetPointer<const ::flatbuffers::String *>(VT_VALUE);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_VALUE) &&
verifier.VerifyString(value()) &&
verifier.EndTable();
}
};
struct ResponseBBuilder {
typedef ResponseB Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_value(::flatbuffers::Offset<::flatbuffers::String> value) {
fbb_.AddOffset(ResponseB::VT_VALUE, value);
}
explicit ResponseBBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<ResponseB> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<ResponseB>(end);
return o;
}
};
inline ::flatbuffers::Offset<ResponseB> CreateResponseB(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> value = 0) {
ResponseBBuilder builder_(_fbb);
builder_.add_value(value);
return builder_.Finish();
}
inline ::flatbuffers::Offset<ResponseB> CreateResponseBDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *value = nullptr) {
auto value__ = value ? _fbb.CreateString(value) : 0;
return MyService::Topic::CreateResponseB(
_fbb,
value__);
}
struct RequestC FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef RequestCBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_COMMAND = 4
};
const ::flatbuffers::String *command() const {
return GetPointer<const ::flatbuffers::String *>(VT_COMMAND);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_COMMAND) &&
verifier.VerifyString(command()) &&
verifier.EndTable();
}
};
struct RequestCBuilder {
typedef RequestC Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_command(::flatbuffers::Offset<::flatbuffers::String> command) {
fbb_.AddOffset(RequestC::VT_COMMAND, command);
}
explicit RequestCBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<RequestC> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<RequestC>(end);
return o;
}
};
inline ::flatbuffers::Offset<RequestC> CreateRequestC(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> command = 0) {
RequestCBuilder builder_(_fbb);
builder_.add_command(command);
return builder_.Finish();
}
inline ::flatbuffers::Offset<RequestC> CreateRequestCDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *command = nullptr) {
auto command__ = command ? _fbb.CreateString(command) : 0;
return MyService::Topic::CreateRequestC(
_fbb,
command__);
}
struct ResponseC FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef ResponseCBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_VALUE = 4
};
const ::flatbuffers::String *value() const {
return GetPointer<const ::flatbuffers::String *>(VT_VALUE);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_VALUE) &&
verifier.VerifyString(value()) &&
verifier.EndTable();
}
};
struct ResponseCBuilder {
typedef ResponseC Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_value(::flatbuffers::Offset<::flatbuffers::String> value) {
fbb_.AddOffset(ResponseC::VT_VALUE, value);
}
explicit ResponseCBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<ResponseC> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<ResponseC>(end);
return o;
}
};
inline ::flatbuffers::Offset<ResponseC> CreateResponseC(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> value = 0) {
ResponseCBuilder builder_(_fbb);
builder_.add_value(value);
return builder_.Finish();
}
inline ::flatbuffers::Offset<ResponseC> CreateResponseCDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *value = nullptr) {
auto value__ = value ? _fbb.CreateString(value) : 0;
return MyService::Topic::CreateResponseC(
_fbb,
value__);
}
struct Message FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef MessageBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_MESSAGE_TYPE = 4,
VT_REQUEST_TYPE = 6,
VT_REQUEST = 8,
VT_RESPONSE_TYPE = 10,
VT_RESPONSE = 12
};
MyService::Topic::RequestType message_type() const {
return static_cast<MyService::Topic::RequestType>(GetField<int8_t>(VT_MESSAGE_TYPE, 0));
}
MyService::Topic::AnyRequest request_type() const {
return static_cast<MyService::Topic::AnyRequest>(GetField<uint8_t>(VT_REQUEST_TYPE, 0));
}
const void *request() const {
return GetPointer<const void *>(VT_REQUEST);
}
template<typename T> const T *request_as() const;
const MyService::Topic::RequestA *request_as_RequestA() const {
return request_type() == MyService::Topic::AnyRequest_RequestA ? static_cast<const MyService::Topic::RequestA *>(request()) : nullptr;
}
const MyService::Topic::RequestB *request_as_RequestB() const {
return request_type() == MyService::Topic::AnyRequest_RequestB ? static_cast<const MyService::Topic::RequestB *>(request()) : nullptr;
}
const MyService::Topic::RequestC *request_as_RequestC() const {
return request_type() == MyService::Topic::AnyRequest_RequestC ? static_cast<const MyService::Topic::RequestC *>(request()) : nullptr;
}
MyService::Topic::AnyResponse response_type() const {
return static_cast<MyService::Topic::AnyResponse>(GetField<uint8_t>(VT_RESPONSE_TYPE, 0));
}
const void *response() const {
return GetPointer<const void *>(VT_RESPONSE);
}
template<typename T> const T *response_as() const;
const MyService::Topic::ResponseA *response_as_ResponseA() const {
return response_type() == MyService::Topic::AnyResponse_ResponseA ? static_cast<const MyService::Topic::ResponseA *>(response()) : nullptr;
}
const MyService::Topic::ResponseB *response_as_ResponseB() const {
return response_type() == MyService::Topic::AnyResponse_ResponseB ? static_cast<const MyService::Topic::ResponseB *>(response()) : nullptr;
}
const MyService::Topic::ResponseC *response_as_ResponseC() const {
return response_type() == MyService::Topic::AnyResponse_ResponseC ? static_cast<const MyService::Topic::ResponseC *>(response()) : nullptr;
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int8_t>(verifier, VT_MESSAGE_TYPE, 1) &&
VerifyField<uint8_t>(verifier, VT_REQUEST_TYPE, 1) &&
VerifyOffset(verifier, VT_REQUEST) &&
VerifyAnyRequest(verifier, request(), request_type()) &&
VerifyField<uint8_t>(verifier, VT_RESPONSE_TYPE, 1) &&
VerifyOffset(verifier, VT_RESPONSE) &&
VerifyAnyResponse(verifier, response(), response_type()) &&
verifier.EndTable();
}
};
template<> inline const MyService::Topic::RequestA *Message::request_as<MyService::Topic::RequestA>() const {
return request_as_RequestA();
}
template<> inline const MyService::Topic::RequestB *Message::request_as<MyService::Topic::RequestB>() const {
return request_as_RequestB();
}
template<> inline const MyService::Topic::RequestC *Message::request_as<MyService::Topic::RequestC>() const {
return request_as_RequestC();
}
template<> inline const MyService::Topic::ResponseA *Message::response_as<MyService::Topic::ResponseA>() const {
return response_as_ResponseA();
}
template<> inline const MyService::Topic::ResponseB *Message::response_as<MyService::Topic::ResponseB>() const {
return response_as_ResponseB();
}
template<> inline const MyService::Topic::ResponseC *Message::response_as<MyService::Topic::ResponseC>() const {
return response_as_ResponseC();
}
struct MessageBuilder {
typedef Message Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_message_type(MyService::Topic::RequestType message_type) {
fbb_.AddElement<int8_t>(Message::VT_MESSAGE_TYPE, static_cast<int8_t>(message_type), 0);
}
void add_request_type(MyService::Topic::AnyRequest request_type) {
fbb_.AddElement<uint8_t>(Message::VT_REQUEST_TYPE, static_cast<uint8_t>(request_type), 0);
}
void add_request(::flatbuffers::Offset<void> request) {
fbb_.AddOffset(Message::VT_REQUEST, request);
}
void add_response_type(MyService::Topic::AnyResponse response_type) {
fbb_.AddElement<uint8_t>(Message::VT_RESPONSE_TYPE, static_cast<uint8_t>(response_type), 0);
}
void add_response(::flatbuffers::Offset<void> response) {
fbb_.AddOffset(Message::VT_RESPONSE, response);
}
explicit MessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Message> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Message>(end);
return o;
}
};
inline ::flatbuffers::Offset<Message> CreateMessage(
::flatbuffers::FlatBufferBuilder &_fbb,
MyService::Topic::RequestType message_type = MyService::Topic::RequestType_RequestA,
MyService::Topic::AnyRequest request_type = MyService::Topic::AnyRequest_NONE,
::flatbuffers::Offset<void> request = 0,
MyService::Topic::AnyResponse response_type = MyService::Topic::AnyResponse_NONE,
::flatbuffers::Offset<void> response = 0) {
MessageBuilder builder_(_fbb);
builder_.add_response(response);
builder_.add_request(request);
builder_.add_response_type(response_type);
builder_.add_request_type(request_type);
builder_.add_message_type(message_type);
return builder_.Finish();
}
inline bool VerifyAnyRequest(::flatbuffers::Verifier &verifier, const void *obj, AnyRequest type) {
switch (type) {
case AnyRequest_NONE: {
return true;
}
case AnyRequest_RequestA: {
auto ptr = reinterpret_cast<const MyService::Topic::RequestA *>(obj);
return verifier.VerifyTable(ptr);
}
case AnyRequest_RequestB: {
auto ptr = reinterpret_cast<const MyService::Topic::RequestB *>(obj);
return verifier.VerifyTable(ptr);
}
case AnyRequest_RequestC: {
auto ptr = reinterpret_cast<const MyService::Topic::RequestC *>(obj);
return verifier.VerifyTable(ptr);
}
default: return true;
}
}
inline bool VerifyAnyRequestVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<uint8_t> *types) {
if (!values || !types) return !values && !types;
if (values->size() != types->size()) return false;
for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
if (!VerifyAnyRequest(
verifier, values->Get(i), types->GetEnum<AnyRequest>(i))) {
return false;
}
}
return true;
}
inline bool VerifyAnyResponse(::flatbuffers::Verifier &verifier, const void *obj, AnyResponse type) {
switch (type) {
case AnyResponse_NONE: {
return true;
}
case AnyResponse_ResponseA: {
auto ptr = reinterpret_cast<const MyService::Topic::ResponseA *>(obj);
return verifier.VerifyTable(ptr);
}
case AnyResponse_ResponseB: {
auto ptr = reinterpret_cast<const MyService::Topic::ResponseB *>(obj);
return verifier.VerifyTable(ptr);
}
case AnyResponse_ResponseC: {
auto ptr = reinterpret_cast<const MyService::Topic::ResponseC *>(obj);
return verifier.VerifyTable(ptr);
}
default: return true;
}
}
inline bool VerifyAnyResponseVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<uint8_t> *types) {
if (!values || !types) return !values && !types;
if (values->size() != types->size()) return false;
for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
if (!VerifyAnyResponse(
verifier, values->Get(i), types->GetEnum<AnyResponse>(i))) {
return false;
}
}
return true;
}
inline const MyService::Topic::Message *GetMessage(const void *buf) {
return ::flatbuffers::GetRoot<MyService::Topic::Message>(buf);
}
inline const MyService::Topic::Message *GetSizePrefixedMessage(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<MyService::Topic::Message>(buf);
}
inline bool VerifyMessageBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<MyService::Topic::Message>(nullptr);
}
inline bool VerifySizePrefixedMessageBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<MyService::Topic::Message>(nullptr);
}
inline void FinishMessageBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<MyService::Topic::Message> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedMessageBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<MyService::Topic::Message> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace Topic
} // namespace MyService
#endif // FLATBUFFERS_GENERATED_MULTIREQUEST_MYSERVICE_TOPIC_H_
Receiving the message
// Assuming `received_buffer` is the buffer you've received
auto message = flatbuffers::GetRoot<Message>(received_buffer);
switch (message->request_type()) {
case AnyRequest_RequestA: {
auto requestA = message->request_as_RequestA();
// Handle RequestA
break;
}
case AnyRequest_RequestB: {
auto requestB = message->request_as_RequestB();
// Handle RequestB
break;
}
case AnyRequest_RequestC: {
auto requestC = message->request_as_RequestC();
// Handle RequestC
break;
}
default:
// Unknown or unhandled request type
break;
}
Conclusion
This looks quite promising; I'll add an update once I've used it in a real-life system.