Skip to content

Commit

Permalink
Defined CodeGenerator Interface and implement C++ (#7771)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaileychess authored Jan 8, 2023
1 parent 641fbe4 commit 3b8644d
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 14 deletions.
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ filegroup(
"include/flatbuffers/bfbs_generator.h",
"include/flatbuffers/buffer.h",
"include/flatbuffers/buffer_ref.h",
"include/flatbuffers/code_generator.h",
"include/flatbuffers/code_generators.h",
"include/flatbuffers/default_allocator.h",
"include/flatbuffers/detached_buffer.h",
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ set(FlatBuffers_Library_SRCS
include/flatbuffers/buffer_ref.h
include/flatbuffers/default_allocator.h
include/flatbuffers/detached_buffer.h
include/flatbuffers/code_generator.h
include/flatbuffers/flatbuffer_builder.h
include/flatbuffers/flatbuffers.h
include/flatbuffers/flexbuffers.h
Expand Down
73 changes: 73 additions & 0 deletions include/flatbuffers/code_generator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef FLATBUFFERS_CODE_GENERATOR_H_
#define FLATBUFFERS_CODE_GENERATOR_H_

#include <string>

#include "flatbuffers/idl.h"

namespace flatbuffers {

// An code generator interface for producing converting flatbuffer schema into
// code.
class CodeGenerator {
public:
virtual ~CodeGenerator() = default;

enum Status {
OK = 0,
ERROR = 1,
NOT_IMPLEMENTED = 2,
};

// Generate code from the provided `parser`.
//
// DEPRECATED: prefer using the other overload of GenerateCode for bfbs.
virtual Status GenerateCode(const Parser &parser, const std::string &path,
const std::string &filename) = 0;

// Generate code from the provided `buffer` of given `length`. The buffer is a
// serialized reflection.fbs.
virtual Status GenerateCode(const uint8_t *buffer, int64_t length) = 0;

virtual Status GenerateMakeRule(const Parser &parser, const std::string &path,
const std::string &filename,
std::string &output) = 0;

virtual Status GenerateGrpcCode(const Parser &parser, const std::string &path,
const std::string &filename) = 0;

virtual bool IsSchemaOnly() const = 0;

virtual bool SupportsBfbsGeneration() const = 0;

virtual IDLOptions::Language Language() const = 0;
virtual std::string LanguageName() const = 0;

protected:
CodeGenerator() = default;

private:
// Copying is not supported.
CodeGenerator(const CodeGenerator &) = delete;
CodeGenerator &operator=(const CodeGenerator &) = delete;
};

} // namespace flatbuffers

#endif // FLATBUFFERS_CODE_GENERATOR_H_
19 changes: 16 additions & 3 deletions include/flatbuffers/flatc.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,21 @@
#include <functional>
#include <limits>
#include <list>
#include <memory>
#include <string>

#include "flatbuffers/bfbs_generator.h"
#include "flatbuffers/code_generator.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"

namespace flatbuffers {

// TODO(derekbailey): It would be better to define these as normal includes and
// not as extern functions. But this can be done at a later time.
extern std::unique_ptr<flatbuffers::CodeGenerator> NewCppCodeGenerator();

extern void LogCompilerWarn(const std::string &warn);
extern void LogCompilerError(const std::string &err);

Expand All @@ -54,6 +60,8 @@ struct FlatCOptions {
bool schema_binary = false;
bool grpc_enabled = false;
bool requires_bfbs = false;

std::vector<std::shared_ptr<CodeGenerator>> generators;
};

struct FlatCOption {
Expand Down Expand Up @@ -110,10 +118,13 @@ class FlatCompiler {

explicit FlatCompiler(const InitParams &params) : params_(params) {}

bool RegisterCodeGenerator(const std::string& flag,
std::shared_ptr<CodeGenerator> code_generator);

int Compile(const FlatCOptions &options);

std::string GetShortUsageString(const std::string& program_name) const;
std::string GetUsageString(const std::string& program_name) const;
std::string GetShortUsageString(const std::string &program_name) const;
std::string GetUsageString(const std::string &program_name) const;

// Parse the FlatC options from command line arguments.
FlatCOptions ParseFromCommandLineArguments(int argc, const char **argv);
Expand Down Expand Up @@ -141,7 +152,9 @@ class FlatCompiler {
Parser GetConformParser(const FlatCOptions &options);

std::unique_ptr<Parser> GenerateCode(const FlatCOptions &options,
Parser &conform_parser);
Parser &conform_parser);

std::map<std::string, std::shared_ptr<CodeGenerator>> code_generators_;

InitParams params_;
};
Expand Down
107 changes: 96 additions & 11 deletions src/flatc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "annotated_binary_text_gen.h"
#include "binary_annotator.h"
#include "flatbuffers/code_generator.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"

Expand Down Expand Up @@ -308,6 +309,7 @@ std::string FlatCompiler::GetShortUsageString(
const std::string &program_name) const {
std::stringstream ss;
ss << "Usage: " << program_name << " [";
// TODO(derekbailey): These should be generated from this.generators
for (size_t i = 0; i < params_.num_generators; ++i) {
const Generator &g = params_.generators[i];
AppendShortOption(ss, g.option);
Expand All @@ -330,6 +332,7 @@ std::string FlatCompiler::GetUsageString(
std::stringstream ss;
ss << "Usage: " << program_name
<< " [OPTION]... FILE... [-- BINARY_FILE...]\n";
// TODO(derekbailey): These should be generated from this.generators
for (size_t i = 0; i < params_.num_generators; ++i) {
const Generator &g = params_.generators[i];
AppendOption(ss, g.option, 80, 25);
Expand Down Expand Up @@ -613,20 +616,41 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc,
if (++argi >= argc) Error("missing path following: " + arg, true);
options.annotate_schema = flatbuffers::PosixPath(argv[argi]);
} else {
for (size_t i = 0; i < params_.num_generators; ++i) {
if (arg == "--" + params_.generators[i].option.long_opt ||
arg == "-" + params_.generators[i].option.short_opt) {
options.generator_enabled[i] = true;
options.any_generator = true;
opts.lang_to_generate |= params_.generators[i].lang;
if (params_.generators[i].bfbs_generator) {
opts.binary_schema_comments = true;
options.requires_bfbs = true;
// Look up if the command line argument refers to a code generator.
auto code_generator_it = code_generators_.find(arg);
if (code_generator_it != code_generators_.end()) {
std::shared_ptr<CodeGenerator> code_generator =
code_generator_it->second;

// TODO(derekbailey): remove in favor of just checking if
// generators.empty().
options.any_generator = true;
opts.lang_to_generate |= code_generator->Language();

if (code_generator->SupportsBfbsGeneration()) {
opts.binary_schema_comments = true;
options.requires_bfbs = true;
}

options.generators.push_back(std::move(code_generator));
} else {
// TODO(derekbailey): deprecate the following logic in favor of the
// code generator map above.
for (size_t i = 0; i < params_.num_generators; ++i) {
if (arg == "--" + params_.generators[i].option.long_opt ||
arg == "-" + params_.generators[i].option.short_opt) {
options.generator_enabled[i] = true;
options.any_generator = true;
opts.lang_to_generate |= params_.generators[i].lang;
if (params_.generators[i].bfbs_generator) {
opts.binary_schema_comments = true;
options.requires_bfbs = true;
}
goto found;
}
goto found;
}
Error("unknown commandline argument: " + arg, true);
}
Error("unknown commandline argument: " + arg, true);

found:;
}
Expand Down Expand Up @@ -789,6 +813,57 @@ std::unique_ptr<Parser> FlatCompiler::GenerateCode(const FlatCOptions &options,
bfbs_length = parser->builder_.GetSize();
}

for (const std::shared_ptr<CodeGenerator> &code_generator :
options.generators) {
if (options.print_make_rules) {
std::string make_rule;
const CodeGenerator::Status status = code_generator->GenerateMakeRule(
*parser, options.output_path, filename, make_rule);
if (status == CodeGenerator::Status::OK && !make_rule.empty()) {
printf("%s\n",
flatbuffers::WordWrap(make_rule, 80, " ", " \\").c_str());
} else {
Error("Cannot generate make rule for " +
code_generator->LanguageName());
}
} else {
flatbuffers::EnsureDirExists(options.output_path);

// Prefer bfbs generators if present.
if (code_generator->SupportsBfbsGeneration()) {
const CodeGenerator::Status status =
code_generator->GenerateCode(bfbs_buffer, bfbs_length);
if (status != CodeGenerator::Status::OK) {
Error("Unable to generate " + code_generator->LanguageName() +
" for " + filebase + " using bfbs generator.");
}
} else {
if ((!code_generator->IsSchemaOnly() ||
(is_schema || is_binary_schema)) &&
code_generator->GenerateCode(*parser, options.output_path,
filebase) !=
CodeGenerator::Status::OK) {
Error("Unable to generate " + code_generator->LanguageName() +
" for " + filebase);
}
}
}

if (options.grpc_enabled) {
const CodeGenerator::Status status = code_generator->GenerateGrpcCode(
*parser, options.output_path, filebase);

if (status == CodeGenerator::Status::NOT_IMPLEMENTED) {
Warn("GRPC interface generator not implemented for " +
code_generator->LanguageName());
} else if (status == CodeGenerator::Status::ERROR) {
Error("Unable to generate GRPC interface for " +
code_generator->LanguageName());
}
}
}

// TODO(derekbailey): Deprecate the following in favor to the above.
for (size_t i = 0; i < params_.num_generators; ++i) {
if (options.generator_enabled[i]) {
if (!options.print_make_rules) {
Expand Down Expand Up @@ -934,4 +1009,14 @@ int FlatCompiler::Compile(const FlatCOptions &options) {
return 0;
}

bool FlatCompiler::RegisterCodeGenerator(
const std::string &flag, std::shared_ptr<CodeGenerator> code_generator) {
if (code_generators_.find(flag) != code_generators_.end()) {
Error("multiple generators registered under: " + flag, false, false);
return false;
}
code_generators_[flag] = std::move(code_generator);
return true;
}

} // namespace flatbuffers
9 changes: 9 additions & 0 deletions src/flatc_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
#include "bfbs_gen_lua.h"
#include "bfbs_gen_nim.h"
#include "flatbuffers/base.h"
#include "flatbuffers/code_generator.h"
#include "flatbuffers/flatc.h"
#include "flatbuffers/util.h"



static const char *g_program_name = nullptr;

static void Warn(const flatbuffers::FlatCompiler *flatc,
Expand Down Expand Up @@ -159,6 +162,12 @@ int main(int argc, const char *argv[]) {

flatbuffers::FlatCompiler flatc(params);

std::shared_ptr<flatbuffers::CodeGenerator> cpp_generator =
flatbuffers::NewCppCodeGenerator();

flatc.RegisterCodeGenerator("--cpp", cpp_generator);
flatc.RegisterCodeGenerator("-c", cpp_generator);

// Create the FlatC options by parsing the command line arguments.
const flatbuffers::FlatCOptions &options =
flatc.ParseFromCommandLineArguments(argc, argv);
Expand Down
47 changes: 47 additions & 0 deletions src/idl_gen_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// independent from idl_parser, since this code is not needed for most clients

#include <limits>
#include <memory>
#include <string>
#include <unordered_set>

Expand Down Expand Up @@ -3871,4 +3872,50 @@ std::string CPPMakeRule(const Parser &parser, const std::string &path,
return make_rule;
}

namespace {

class CppCodeGenerator : public CodeGenerator {
public:
Status GenerateCode(const Parser &parser, const std::string &path,
const std::string &filename) override {
if (!GenerateCPP(parser, path, filename)) { return Status::ERROR; }
return Status::OK;
}

// Generate code from the provided `buffer` of given `length`. The buffer is a
// serialized reflection.fbs.
Status GenerateCode(const uint8_t *buffer, int64_t length) override {
(void) buffer;
(void) length;
return Status::NOT_IMPLEMENTED;
}

Status GenerateMakeRule(const Parser &parser, const std::string &path,
const std::string &filename,
std::string &output) override {
output = CPPMakeRule(parser, path, filename);
return Status::OK;
}

Status GenerateGrpcCode(const Parser &parser, const std::string &path,
const std::string &filename) override {
if (!GenerateCppGRPC(parser, path, filename)) { return Status::ERROR; }
return Status::OK;
}

bool IsSchemaOnly() const override { return true; }

bool SupportsBfbsGeneration() const override { return false; }

IDLOptions::Language Language() const override { return IDLOptions::kCpp; }

std::string LanguageName() const override { return "C++"; }
};

} // namespace

std::unique_ptr<CodeGenerator> NewCppCodeGenerator() {
return std::unique_ptr<CppCodeGenerator>(new CppCodeGenerator());
}

} // namespace flatbuffers

0 comments on commit 3b8644d

Please sign in to comment.