forked from BigfootDev/flatbuffers
String default values parsed from .fbs schemas are un-escaped by the IDL parser (e.g., \x22 becomes a raw " byte), but code generators embed these raw values directly into generated source code string literals. This allows specially crafted .fbs files to break out of string literals and inject arbitrary code into generated C++, Rust, TypeScript, and Swift source. Fix by adding EscapeCodeGenString() helper that re-escapes string content before embedding, and applying it to all 7 affected injection points across 5 code generators (C++, Rust, TypeScript, Swift, FBS). Resolves the TODO comments in idl_gen_cpp.cpp and idl_gen_rust.cpp.
4614 lines
170 KiB
C++
4614 lines
170 KiB
C++
/*
|
|
* Copyright 2014 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// independent from idl_parser, since this code is not needed for most clients
|
|
|
|
#include "idl_gen_cpp.h"
|
|
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
|
|
#include "flatbuffers/base.h"
|
|
#include "flatbuffers/code_generators.h"
|
|
#include "flatbuffers/flatbuffers.h"
|
|
#include "flatbuffers/flatc.h"
|
|
#include "flatbuffers/idl.h"
|
|
#include "flatbuffers/util.h"
|
|
|
|
namespace flatbuffers {
|
|
|
|
// Make numerical literal with type-suffix.
|
|
// This function is only needed for C++! Other languages do not need it.
|
|
static inline std::string NumToStringCpp(std::string val, BaseType type) {
|
|
// Avoid issues with -2147483648, -9223372036854775808.
|
|
switch (type) {
|
|
case BASE_TYPE_INT:
|
|
return (val != "-2147483648") ? val : ("(-2147483647 - 1)");
|
|
case BASE_TYPE_ULONG:
|
|
return (val == "0") ? val : (val + "ULL");
|
|
case BASE_TYPE_LONG:
|
|
if (val == "-9223372036854775808")
|
|
return "(-9223372036854775807LL - 1LL)";
|
|
else
|
|
return (val == "0") ? val : (val + "LL");
|
|
default:
|
|
return val;
|
|
}
|
|
}
|
|
|
|
static std::string GenIncludeGuard(const std::string& file_name,
|
|
const Namespace& name_space,
|
|
const std::string& postfix = "") {
|
|
// Generate include guard.
|
|
std::string guard = file_name;
|
|
// Remove any non-alpha-numeric characters that may appear in a filename.
|
|
struct IsAlnum {
|
|
bool operator()(char c) const { return !is_alnum(c); }
|
|
};
|
|
guard.erase(std::remove_if(guard.begin(), guard.end(), IsAlnum()),
|
|
guard.end());
|
|
guard = "FLATBUFFERS_GENERATED_" + guard;
|
|
guard += "_";
|
|
// For further uniqueness, also add the namespace.
|
|
for (const std::string& component : name_space.components) {
|
|
guard += component + "_";
|
|
}
|
|
// Anything extra to add to the guard?
|
|
if (!postfix.empty()) {
|
|
guard += postfix + "_";
|
|
}
|
|
guard += "H_";
|
|
std::transform(guard.begin(), guard.end(), guard.begin(), CharToUpper);
|
|
return guard;
|
|
}
|
|
|
|
static bool IsVectorOfPointers(const FieldDef& field) {
|
|
const auto& type = field.value.type;
|
|
const auto& vector_type = type.VectorType();
|
|
return IsVector(type) && vector_type.base_type == BASE_TYPE_STRUCT &&
|
|
!vector_type.struct_def->fixed && !field.native_inline;
|
|
}
|
|
|
|
static bool IsPointer(const FieldDef& field) {
|
|
return field.value.type.base_type == BASE_TYPE_STRUCT &&
|
|
!IsStruct(field.value.type);
|
|
}
|
|
|
|
namespace cpp {
|
|
|
|
enum CppStandard { CPP_STD_X0 = 0, CPP_STD_11, CPP_STD_17 };
|
|
|
|
// Define a style of 'struct' constructor if it has 'Array' fields.
|
|
enum GenArrayArgMode {
|
|
kArrayArgModeNone, // don't generate initialization args
|
|
kArrayArgModeSpanStatic, // generate ::flatbuffers::span<T,N>
|
|
};
|
|
|
|
// Extension of IDLOptions for cpp-generator.
|
|
struct IDLOptionsCpp : public IDLOptions {
|
|
// All fields start with 'g_' prefix to distinguish from the base IDLOptions.
|
|
CppStandard g_cpp_std; // Base version of C++ standard.
|
|
bool g_only_fixed_enums; // Generate underlaying type for all enums.
|
|
|
|
IDLOptionsCpp(const IDLOptions& opts)
|
|
: IDLOptions(opts), g_cpp_std(CPP_STD_11), g_only_fixed_enums(true) {}
|
|
};
|
|
|
|
// Iterates over all the fields of the object first by Offset type (Offset64
|
|
// before Offset32) and then by definition order.
|
|
static void ForAllFieldsOrderedByOffset(
|
|
const StructDef& object, std::function<void(const FieldDef* field)> func) {
|
|
// Loop over all the fields and call the func on all offset64 fields.
|
|
for (const FieldDef* field_def : object.fields.vec) {
|
|
if (field_def->offset64) {
|
|
func(field_def);
|
|
}
|
|
}
|
|
// Loop over all the fields a second time and call the func on all offset
|
|
// fields.
|
|
for (const FieldDef* field_def : object.fields.vec) {
|
|
if (!field_def->offset64) {
|
|
func(field_def);
|
|
}
|
|
}
|
|
}
|
|
|
|
class CppGenerator : public BaseGenerator {
|
|
public:
|
|
CppGenerator(const Parser& parser, const std::string& path,
|
|
const std::string& file_name, IDLOptionsCpp opts)
|
|
: BaseGenerator(parser, path, file_name, "", "::", "h"),
|
|
cur_name_space_(nullptr),
|
|
opts_(opts),
|
|
float_const_gen_("std::numeric_limits<double>::",
|
|
"std::numeric_limits<float>::", "quiet_NaN()",
|
|
"infinity()") {
|
|
static const char* const keywords[] = {
|
|
"alignas",
|
|
"alignof",
|
|
"and",
|
|
"and_eq",
|
|
"asm",
|
|
"atomic_cancel",
|
|
"atomic_commit",
|
|
"atomic_noexcept",
|
|
"auto",
|
|
"bitand",
|
|
"bitor",
|
|
"bool",
|
|
"break",
|
|
"case",
|
|
"catch",
|
|
"char",
|
|
"char16_t",
|
|
"char32_t",
|
|
"class",
|
|
"compl",
|
|
"concept",
|
|
"const",
|
|
"constexpr",
|
|
"const_cast",
|
|
"continue",
|
|
"co_await",
|
|
"co_return",
|
|
"co_yield",
|
|
"decltype",
|
|
"default",
|
|
"delete",
|
|
"do",
|
|
"double",
|
|
"dynamic_cast",
|
|
"else",
|
|
"enum",
|
|
"explicit",
|
|
"export",
|
|
"extern",
|
|
"false",
|
|
"float",
|
|
"for",
|
|
"friend",
|
|
"goto",
|
|
"if",
|
|
"import",
|
|
"inline",
|
|
"int",
|
|
"long",
|
|
"module",
|
|
"mutable",
|
|
"namespace",
|
|
"new",
|
|
"noexcept",
|
|
"not",
|
|
"not_eq",
|
|
"nullptr",
|
|
"operator",
|
|
"or",
|
|
"or_eq",
|
|
"private",
|
|
"protected",
|
|
"public",
|
|
"register",
|
|
"reinterpret_cast",
|
|
"requires",
|
|
"return",
|
|
"short",
|
|
"signed",
|
|
"sizeof",
|
|
"static",
|
|
"static_assert",
|
|
"static_cast",
|
|
"struct",
|
|
"switch",
|
|
"synchronized",
|
|
"template",
|
|
"this",
|
|
"thread_local",
|
|
"throw",
|
|
"true",
|
|
"try",
|
|
"typedef",
|
|
"typeid",
|
|
"typename",
|
|
"union",
|
|
"unsigned",
|
|
"using",
|
|
"virtual",
|
|
"void",
|
|
"volatile",
|
|
"wchar_t",
|
|
"while",
|
|
"xor",
|
|
"xor_eq",
|
|
nullptr,
|
|
};
|
|
for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
|
|
}
|
|
|
|
// Adds code to check that the included flatbuffers.h is of the same version
|
|
// as the generated code. This check currently looks for exact version match,
|
|
// as we would guarantee that they are compatible, but in theory a newer
|
|
// version of flatbuffers.h should work with a old code gen if we do proper
|
|
// backwards support.
|
|
void GenFlatbuffersVersionCheck() {
|
|
code_ +=
|
|
"// Ensure the included flatbuffers.h is the same version as when this "
|
|
"file was";
|
|
code_ += "// generated, otherwise it may not be compatible.";
|
|
code_ += "static_assert(FLATBUFFERS_VERSION_MAJOR == " +
|
|
std::to_string(FLATBUFFERS_VERSION_MAJOR) + " &&";
|
|
code_ += " FLATBUFFERS_VERSION_MINOR == " +
|
|
std::to_string(FLATBUFFERS_VERSION_MINOR) + " &&";
|
|
code_ += " FLATBUFFERS_VERSION_REVISION == " +
|
|
std::to_string(FLATBUFFERS_VERSION_REVISION) + ",";
|
|
code_ += " \"Non-compatible flatbuffers version included\");";
|
|
}
|
|
|
|
void GenIncludeDependencies() {
|
|
if (opts_.generate_object_based_api) {
|
|
for (const std::string& native_included_file :
|
|
parser_.native_included_files_) {
|
|
code_ += "#include \"" + native_included_file + "\"";
|
|
}
|
|
}
|
|
|
|
// Get the directly included file of the file being parsed.
|
|
std::vector<IncludedFile> included_files(parser_.GetIncludedFiles());
|
|
|
|
// We are safe to sort them alphabetically, since there shouldn't be any
|
|
// interdependence between them.
|
|
std::stable_sort(included_files.begin(), included_files.end());
|
|
|
|
for (const IncludedFile& included_file : included_files) {
|
|
// Get the name of the included file as defined by the schema, and strip
|
|
// the .fbs extension.
|
|
const std::string name_without_ext =
|
|
StripExtension(included_file.schema_name);
|
|
|
|
// If we are told to keep the prefix of the included schema, leave it
|
|
// unchanged, otherwise strip the leading path off so just the "basename"
|
|
// of the include is retained.
|
|
const std::string basename =
|
|
opts_.keep_prefix ? name_without_ext : StripPath(name_without_ext);
|
|
|
|
code_ += "#include \"" +
|
|
GeneratedFileName(opts_.include_prefix, basename, opts_) + "\"";
|
|
}
|
|
|
|
if (!parser_.native_included_files_.empty() || !included_files.empty()) {
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
void MarkIf64BitBuilderIsNeeded() {
|
|
if (needs_64_bit_builder_) {
|
|
return;
|
|
}
|
|
for (auto t : parser_.structs_.vec) {
|
|
if (t == nullptr) continue;
|
|
for (auto f : t->fields.vec) {
|
|
if (f == nullptr) continue;
|
|
if (f->offset64) {
|
|
needs_64_bit_builder_ = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string GetBuilder() {
|
|
return std::string("::flatbuffers::FlatBufferBuilder") +
|
|
(needs_64_bit_builder_ ? "64" : "");
|
|
}
|
|
|
|
void GenExtraIncludes() {
|
|
for (const std::string& cpp_include : opts_.cpp_includes) {
|
|
code_ += "#include \"" + cpp_include + "\"";
|
|
}
|
|
if (!opts_.cpp_includes.empty()) {
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
void GenEmbeddedIncludes() {
|
|
if (parser_.opts.binary_schema_gen_embed && parser_.root_struct_def_) {
|
|
const std::string file_path =
|
|
GeneratedFileName(opts_.include_prefix, file_name_ + "_bfbs", opts_);
|
|
code_ += "// For access to the binary schema that produced this file.";
|
|
code_ += "#include \"" + file_path + "\"";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
std::string EscapeKeyword(const std::string& name) const {
|
|
return keywords_.find(name) == keywords_.end() ? name : name + "_";
|
|
}
|
|
|
|
std::string Name(const FieldDef& field) const {
|
|
// the union type field suffix is immutable.
|
|
static size_t union_suffix_len = strlen(UnionTypeFieldSuffix());
|
|
const bool is_union_type = field.value.type.base_type == BASE_TYPE_UTYPE;
|
|
// early return if no case transformation required
|
|
if (opts_.cpp_object_api_field_case_style ==
|
|
IDLOptions::CaseStyle_Unchanged)
|
|
return EscapeKeyword(field.name);
|
|
std::string name = field.name;
|
|
// do not change the case style of the union type field suffix
|
|
if (is_union_type) {
|
|
FLATBUFFERS_ASSERT(name.length() > union_suffix_len);
|
|
name.erase(name.length() - union_suffix_len, union_suffix_len);
|
|
}
|
|
if (opts_.cpp_object_api_field_case_style == IDLOptions::CaseStyle_Upper)
|
|
name = ConvertCase(name, Case::kUpperCamel);
|
|
else if (opts_.cpp_object_api_field_case_style ==
|
|
IDLOptions::CaseStyle_Lower)
|
|
name = ConvertCase(name, Case::kLowerCamel);
|
|
// restore the union field type suffix
|
|
if (is_union_type) name.append(UnionTypeFieldSuffix(), union_suffix_len);
|
|
return EscapeKeyword(name);
|
|
}
|
|
|
|
std::string Name(const Definition& def) const {
|
|
return EscapeKeyword(def.name);
|
|
}
|
|
|
|
std::string Name(const EnumVal& ev) const { return EscapeKeyword(ev.name); }
|
|
|
|
bool generate_bfbs_embed() {
|
|
code_.Clear();
|
|
code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
|
|
|
|
// If we don't have a root struct definition,
|
|
if (!parser_.root_struct_def_) {
|
|
// put a comment in the output why there is no code generated.
|
|
code_ += "// Binary schema not generated, no root struct found";
|
|
} else {
|
|
auto& struct_def = *parser_.root_struct_def_;
|
|
const auto include_guard =
|
|
GenIncludeGuard(file_name_, *struct_def.defined_namespace, "bfbs");
|
|
|
|
code_ += "#ifndef " + include_guard;
|
|
code_ += "#define " + include_guard;
|
|
code_ += "";
|
|
if (parser_.opts.gen_nullable) {
|
|
code_ += "#pragma clang system_header\n\n";
|
|
}
|
|
|
|
code_ += "#include <cstddef>";
|
|
code_ += "#include <cstdint>";
|
|
|
|
SetNameSpace(struct_def.defined_namespace);
|
|
auto name = Name(struct_def);
|
|
code_.SetValue("STRUCT_NAME", name);
|
|
|
|
// Create code to return the binary schema data.
|
|
auto binary_schema_hex_text =
|
|
BufferToHexText(parser_.builder_.GetBufferPointer(),
|
|
parser_.builder_.GetSize(), 105, " ", "");
|
|
|
|
code_ += "struct {{STRUCT_NAME}}BinarySchema {";
|
|
code_ += " static const uint8_t *data() {";
|
|
code_ += " // Buffer containing the binary schema.";
|
|
code_ += " static const uint8_t bfbsData[" +
|
|
NumToString(parser_.builder_.GetSize()) + "] = {";
|
|
code_ += binary_schema_hex_text;
|
|
code_ += " };";
|
|
code_ += " return bfbsData;";
|
|
code_ += " }";
|
|
code_ += " static size_t size() {";
|
|
code_ += " return " + NumToString(parser_.builder_.GetSize()) + ";";
|
|
code_ += " }";
|
|
code_ += " const uint8_t *begin() {";
|
|
code_ += " return data();";
|
|
code_ += " }";
|
|
code_ += " const uint8_t *end() {";
|
|
code_ += " return data() + size();";
|
|
code_ += " }";
|
|
code_ += "};";
|
|
code_ += "";
|
|
|
|
if (cur_name_space_) SetNameSpace(nullptr);
|
|
|
|
// Close the include guard.
|
|
code_ += "#endif // " + include_guard;
|
|
}
|
|
|
|
// We are just adding "_bfbs" to the generated filename.
|
|
const auto file_path =
|
|
GeneratedFileName(path_, file_name_ + "_bfbs", opts_);
|
|
const auto final_code = code_.ToString();
|
|
|
|
return parser_.opts.file_saver->SaveFile(file_path.c_str(), final_code,
|
|
false);
|
|
}
|
|
|
|
// Iterate through all definitions we haven't generate code for (enums,
|
|
// structs, and tables) and output them to a single file.
|
|
bool generate() {
|
|
// Check if we require a 64-bit flatbuffer builder.
|
|
MarkIf64BitBuilderIsNeeded();
|
|
|
|
code_.Clear();
|
|
code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
|
|
|
|
const auto include_guard =
|
|
GenIncludeGuard(file_name_, *parser_.current_namespace_);
|
|
code_ += "#ifndef " + include_guard;
|
|
code_ += "#define " + include_guard;
|
|
code_ += "";
|
|
|
|
if (opts_.gen_nullable) {
|
|
code_ += "#pragma clang system_header\n\n";
|
|
}
|
|
|
|
code_ += "#include \"flatbuffers/flatbuffers.h\"";
|
|
if (parser_.uses_flexbuffers_) {
|
|
code_ += "#include \"flatbuffers/flexbuffers.h\"";
|
|
code_ += "#include \"flatbuffers/flex_flat_util.h\"";
|
|
}
|
|
code_ += "";
|
|
GenFlatbuffersVersionCheck();
|
|
code_ += "";
|
|
|
|
if (opts_.include_dependence_headers) {
|
|
GenIncludeDependencies();
|
|
}
|
|
GenExtraIncludes();
|
|
GenEmbeddedIncludes();
|
|
|
|
FLATBUFFERS_ASSERT(!cur_name_space_);
|
|
|
|
// Generate forward declarations for all structs/tables, since they may
|
|
// have circular references.
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
if (!struct_def->generated) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
code_ += "struct " + Name(*struct_def) + ";";
|
|
if (!struct_def->fixed) {
|
|
code_ += "struct " + Name(*struct_def) + "Builder;";
|
|
}
|
|
if (opts_.generate_object_based_api) {
|
|
auto nativeName = NativeName(Name(*struct_def), struct_def, opts_);
|
|
|
|
// Check that nativeName doesn't collide the name of another struct.
|
|
for (const auto& other_struct_def : parser_.structs_.vec) {
|
|
if (other_struct_def == struct_def ||
|
|
other_struct_def->defined_namespace !=
|
|
struct_def->defined_namespace) {
|
|
continue;
|
|
}
|
|
|
|
auto other_name = Name(*other_struct_def);
|
|
if (nativeName == other_name) {
|
|
LogCompilerError("Generated Object API type for " +
|
|
Name(*struct_def) + " collides with " +
|
|
other_name);
|
|
FLATBUFFERS_ASSERT(true);
|
|
}
|
|
}
|
|
|
|
// Don't declare a new object API struct type if the table is a
|
|
// native type.
|
|
const auto native_type = struct_def->attributes.Lookup("native_type");
|
|
if (!struct_def->fixed && !native_type) {
|
|
code_ += "struct " + nativeName + ";";
|
|
}
|
|
}
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
// Generate forward declarations for all equal operators
|
|
if (opts_.generate_object_based_api && opts_.gen_compare) {
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
const auto native_type = struct_def->attributes.Lookup("native_type");
|
|
if (!struct_def->generated && !native_type) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
auto nativeName = NativeName(Name(*struct_def), struct_def, opts_);
|
|
code_ += "bool operator==(const " + nativeName + " &lhs, const " +
|
|
nativeName + " &rhs);";
|
|
code_ += "bool operator!=(const " + nativeName + " &lhs, const " +
|
|
nativeName + " &rhs);";
|
|
}
|
|
}
|
|
code_ += "";
|
|
}
|
|
|
|
// Generate preablmle code for mini reflection.
|
|
if (opts_.mini_reflect != IDLOptions::kNone) {
|
|
// To break cyclic dependencies, first pre-declare all tables/structs.
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
if (!struct_def->generated) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
GenMiniReflectPre(struct_def);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate code for all the enum declarations.
|
|
for (const auto& enum_def : parser_.enums_.vec) {
|
|
if (!enum_def->generated) {
|
|
SetNameSpace(enum_def->defined_namespace);
|
|
GenEnum(*enum_def);
|
|
}
|
|
}
|
|
|
|
// Generate code for all structs, then all tables.
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
if (struct_def->fixed && !struct_def->generated) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
GenStruct(*struct_def);
|
|
}
|
|
}
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
if (!struct_def->fixed && !struct_def->generated) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
GenTable(*struct_def);
|
|
}
|
|
}
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
if (!struct_def->fixed && !struct_def->generated) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
GenTablePost(*struct_def);
|
|
}
|
|
}
|
|
|
|
// Generate code for union verifiers.
|
|
for (const auto& enum_def : parser_.enums_.vec) {
|
|
if (enum_def->is_union && !enum_def->generated) {
|
|
SetNameSpace(enum_def->defined_namespace);
|
|
GenUnionPost(*enum_def);
|
|
}
|
|
}
|
|
|
|
// Generate code for mini reflection.
|
|
if (opts_.mini_reflect != IDLOptions::kNone) {
|
|
// Then the unions/enums that may refer to them.
|
|
for (const auto& enum_def : parser_.enums_.vec) {
|
|
if (!enum_def->generated) {
|
|
SetNameSpace(enum_def->defined_namespace);
|
|
GenMiniReflect(nullptr, enum_def);
|
|
}
|
|
}
|
|
// Then the full tables/structs.
|
|
for (const auto& struct_def : parser_.structs_.vec) {
|
|
if (!struct_def->generated) {
|
|
SetNameSpace(struct_def->defined_namespace);
|
|
GenMiniReflect(struct_def, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate convenient global helper functions:
|
|
if (parser_.root_struct_def_) {
|
|
auto& struct_def = *parser_.root_struct_def_;
|
|
SetNameSpace(struct_def.defined_namespace);
|
|
auto name = Name(struct_def);
|
|
auto qualified_name = cur_name_space_->GetFullyQualifiedName(name);
|
|
auto cpp_name = TranslateNameSpace(qualified_name);
|
|
|
|
code_.SetValue("STRUCT_NAME", name);
|
|
code_.SetValue("CPP_NAME", cpp_name);
|
|
code_.SetValue("NULLABLE_EXT", NullableExtension());
|
|
code_.SetValue(
|
|
"SIZE_T", needs_64_bit_builder_ ? ",::flatbuffers::uoffset64_t" : "");
|
|
|
|
// The root datatype accessor:
|
|
code_ += "inline \\";
|
|
code_ +=
|
|
"const {{CPP_NAME}} *{{NULLABLE_EXT}}Get{{STRUCT_NAME}}(const void "
|
|
"*buf) {";
|
|
code_ += " return ::flatbuffers::GetRoot<{{CPP_NAME}}>(buf);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "inline \\";
|
|
code_ +=
|
|
"const {{CPP_NAME}} "
|
|
"*{{NULLABLE_EXT}}GetSizePrefixed{{STRUCT_NAME}}(const void "
|
|
"*buf) {";
|
|
code_ +=
|
|
" return "
|
|
"::flatbuffers::GetSizePrefixedRoot<{{CPP_NAME}}{{SIZE_T}}>(buf);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
if (opts_.mutable_buffer) {
|
|
code_ += "inline \\";
|
|
code_ += "{{STRUCT_NAME}} *GetMutable{{STRUCT_NAME}}(void *buf) {";
|
|
code_ +=
|
|
" return ::flatbuffers::GetMutableRoot<{{STRUCT_NAME}}>(buf);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "inline \\";
|
|
code_ +=
|
|
"{{CPP_NAME}} "
|
|
"*{{NULLABLE_EXT}}GetMutableSizePrefixed{{STRUCT_NAME}}(void "
|
|
"*buf) {";
|
|
code_ +=
|
|
" return "
|
|
"::flatbuffers::GetMutableSizePrefixedRoot<{{CPP_NAME}}{{SIZE_T}}>("
|
|
"buf);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
if (parser_.file_identifier_.length()) {
|
|
// Return the identifier
|
|
code_ += "inline const char *{{STRUCT_NAME}}Identifier() {";
|
|
code_ += " return \"" + parser_.file_identifier_ + "\";";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
// Check if a buffer has the identifier.
|
|
code_ += "inline \\";
|
|
code_ += "bool {{STRUCT_NAME}}BufferHasIdentifier(const void *buf) {";
|
|
code_ += " return ::flatbuffers::BufferHasIdentifier(";
|
|
code_ += " buf, {{STRUCT_NAME}}Identifier());";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
// Check if a size-prefixed buffer has the identifier.
|
|
code_ += "inline \\";
|
|
code_ +=
|
|
"bool SizePrefixed{{STRUCT_NAME}}BufferHasIdentifier(const void "
|
|
"*buf) {";
|
|
code_ += " return ::flatbuffers::BufferHasIdentifier(";
|
|
code_ += " buf, {{STRUCT_NAME}}Identifier(), true);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
// The root verifier.
|
|
if (parser_.file_identifier_.length()) {
|
|
code_.SetValue("ID", name + "Identifier()");
|
|
} else {
|
|
code_.SetValue("ID", "nullptr");
|
|
}
|
|
|
|
code_ += "template <bool B = false>";
|
|
code_ += "inline bool Verify{{STRUCT_NAME}}Buffer(";
|
|
code_ += " ::flatbuffers::VerifierTemplate<B> &verifier) {";
|
|
code_ += " return verifier.template VerifyBuffer<{{CPP_NAME}}>({{ID}});";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "template <bool B = false>";
|
|
code_ += "inline bool VerifySizePrefixed{{STRUCT_NAME}}Buffer(";
|
|
code_ += " ::flatbuffers::VerifierTemplate<B> &verifier) {";
|
|
code_ +=
|
|
" return "
|
|
"verifier.template "
|
|
"VerifySizePrefixedBuffer<{{CPP_NAME}}{{SIZE_T}}>({{ID}});";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
if (parser_.file_extension_.length()) {
|
|
// Return the extension
|
|
code_ += "inline const char *{{STRUCT_NAME}}Extension() {";
|
|
code_ += " return \"" + parser_.file_extension_ + "\";";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
// Finish a buffer with a given root object:
|
|
code_ += "inline void Finish{{STRUCT_NAME}}Buffer(";
|
|
code_ += " " + GetBuilder() + " &fbb,";
|
|
code_ += " ::flatbuffers::Offset<{{CPP_NAME}}> root) {";
|
|
if (parser_.file_identifier_.length())
|
|
code_ += " fbb.Finish(root, {{STRUCT_NAME}}Identifier());";
|
|
else
|
|
code_ += " fbb.Finish(root);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "inline void FinishSizePrefixed{{STRUCT_NAME}}Buffer(";
|
|
code_ += " " + GetBuilder() + " &fbb,";
|
|
code_ += " ::flatbuffers::Offset<{{CPP_NAME}}> root) {";
|
|
if (parser_.file_identifier_.length())
|
|
code_ += " fbb.FinishSizePrefixed(root, {{STRUCT_NAME}}Identifier());";
|
|
else
|
|
code_ += " fbb.FinishSizePrefixed(root);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
if (opts_.generate_object_based_api) {
|
|
// A convenient root unpack function.
|
|
auto native_name = WrapNativeNameInNameSpace(struct_def, opts_);
|
|
code_.SetValue("UNPACK_RETURN",
|
|
GenTypeNativePtr(native_name, nullptr, false));
|
|
code_.SetValue("UNPACK_TYPE",
|
|
GenTypeNativePtr(native_name, nullptr, true));
|
|
|
|
code_ += "inline {{UNPACK_RETURN}} UnPack{{STRUCT_NAME}}(";
|
|
code_ += " const void *buf,";
|
|
code_ +=
|
|
" const ::flatbuffers::resolver_function_t *res = nullptr) {";
|
|
code_ += " return {{UNPACK_TYPE}}\\";
|
|
code_ += "(Get{{STRUCT_NAME}}(buf)->UnPack(res));";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "inline {{UNPACK_RETURN}} UnPackSizePrefixed{{STRUCT_NAME}}(";
|
|
code_ += " const void *buf,";
|
|
code_ +=
|
|
" const ::flatbuffers::resolver_function_t *res = nullptr) {";
|
|
code_ += " return {{UNPACK_TYPE}}\\";
|
|
code_ += "(GetSizePrefixed{{STRUCT_NAME}}(buf)->UnPack(res));";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
if (cur_name_space_) SetNameSpace(nullptr);
|
|
|
|
// Close the include guard.
|
|
code_ += "#endif // " + include_guard;
|
|
|
|
const auto file_path = GeneratedFileName(path_, file_name_, opts_);
|
|
const auto final_code = code_.ToString();
|
|
|
|
// Save the file and optionally generate the binary schema code.
|
|
return parser_.opts.file_saver->SaveFile(
|
|
file_path.c_str(), final_code,
|
|
(!parser_.opts.binary_schema_gen_embed || generate_bfbs_embed()));
|
|
}
|
|
|
|
private:
|
|
CodeWriter code_;
|
|
|
|
std::unordered_set<std::string> keywords_;
|
|
|
|
// This tracks the current namespace so we can insert namespace declarations.
|
|
const Namespace* cur_name_space_;
|
|
|
|
const IDLOptionsCpp opts_;
|
|
const TypedFloatConstantGenerator float_const_gen_;
|
|
bool needs_64_bit_builder_ = false;
|
|
|
|
const Namespace* CurrentNameSpace() const { return cur_name_space_; }
|
|
|
|
// Translates a qualified name in flatbuffer text format to the same name in
|
|
// the equivalent C++ namespace.
|
|
static std::string TranslateNameSpace(const std::string& qualified_name) {
|
|
std::string cpp_qualified_name = qualified_name;
|
|
size_t start_pos = 0;
|
|
while ((start_pos = cpp_qualified_name.find('.', start_pos)) !=
|
|
std::string::npos) {
|
|
cpp_qualified_name.replace(start_pos, 1, "::");
|
|
}
|
|
return cpp_qualified_name;
|
|
}
|
|
|
|
bool TypeHasKey(const Type& type) {
|
|
if (type.base_type != BASE_TYPE_STRUCT) {
|
|
return false;
|
|
}
|
|
for (auto& field : type.struct_def->fields.vec) {
|
|
if (field->key) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VectorElementUserFacing(const Type& type) const {
|
|
return (opts_.scoped_enums && IsEnum(type)) ||
|
|
(opts_.g_cpp_std >= cpp::CPP_STD_17 && opts_.g_only_fixed_enums &&
|
|
IsEnum(type));
|
|
}
|
|
|
|
void GenComment(const std::vector<std::string>& dc, const char* prefix = "") {
|
|
std::string text;
|
|
::flatbuffers::GenComment(dc, &text, nullptr, prefix);
|
|
code_ += text + "\\";
|
|
}
|
|
|
|
// Return a C++ type from the table in idl.h
|
|
std::string GenTypeBasic(const Type& type, bool user_facing_type) const {
|
|
if (user_facing_type) {
|
|
if (type.enum_def) return WrapInNameSpace(*type.enum_def);
|
|
if (type.base_type == BASE_TYPE_BOOL) return "bool";
|
|
}
|
|
// Get real underlying type for union type
|
|
auto base_type = type.base_type;
|
|
if (type.base_type == BASE_TYPE_UTYPE && type.enum_def != nullptr) {
|
|
base_type = type.enum_def->underlying_type.base_type;
|
|
}
|
|
return StringOf(base_type);
|
|
}
|
|
|
|
// Return a C++ pointer type, specialized to the actual struct/table types,
|
|
// and vector element types.
|
|
std::string GenTypePointer(const Type& type) const {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_STRING: {
|
|
return "::flatbuffers::String";
|
|
}
|
|
case BASE_TYPE_VECTOR64:
|
|
case BASE_TYPE_VECTOR: {
|
|
const auto type_name = GenTypeWire(
|
|
type.VectorType(), "", VectorElementUserFacing(type.VectorType()));
|
|
return "::flatbuffers::Vector" +
|
|
std::string((type.base_type == BASE_TYPE_VECTOR64) ? "64<"
|
|
: "<") +
|
|
type_name + ">";
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
return WrapInNameSpace(*type.struct_def);
|
|
}
|
|
case BASE_TYPE_UNION:
|
|
// fall through
|
|
default: {
|
|
return "void";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return a C++ type for any type (scalar/pointer) specifically for
|
|
// building a flatbuffer.
|
|
std::string GenTypeWire(const Type& type, const char* postfix,
|
|
bool user_facing_type,
|
|
bool _64_bit_offset = false) const {
|
|
if (IsScalar(type.base_type)) {
|
|
return GenTypeBasic(type, user_facing_type) + postfix;
|
|
} else if (IsStruct(type)) {
|
|
return "const " + GenTypePointer(type) + " *";
|
|
} else {
|
|
return "::flatbuffers::Offset" + std::string(_64_bit_offset ? "64" : "") +
|
|
"<" + GenTypePointer(type) + ">" + postfix;
|
|
}
|
|
}
|
|
|
|
// Return a C++ type for any type (scalar/pointer) that reflects its
|
|
// serialized size.
|
|
std::string GenTypeSize(const Type& type) const {
|
|
if (IsScalar(type.base_type)) {
|
|
return GenTypeBasic(type, false);
|
|
} else if (IsStruct(type)) {
|
|
return GenTypePointer(type);
|
|
} else {
|
|
return "::flatbuffers::uoffset_t";
|
|
}
|
|
}
|
|
|
|
std::string NullableExtension() {
|
|
return opts_.gen_nullable ? " _Nullable " : "";
|
|
}
|
|
|
|
static std::string NativeName(const std::string& name, const StructDef* sd,
|
|
const IDLOptions& opts) {
|
|
// If the table is a native_type, return the native_type name.
|
|
const auto native_type = sd->attributes.Lookup("native_type");
|
|
if (native_type && !sd->fixed) {
|
|
return native_type->constant;
|
|
}
|
|
|
|
return sd && !sd->fixed ? opts.object_prefix + name + opts.object_suffix
|
|
: name;
|
|
}
|
|
|
|
std::string WrapNativeNameInNameSpace(const StructDef& struct_def,
|
|
const IDLOptions& opts) {
|
|
// If the table is a native_type, return the native_type name.
|
|
const auto native_type = struct_def.attributes.Lookup("native_type");
|
|
if (native_type && !struct_def.fixed) {
|
|
return native_type->constant;
|
|
}
|
|
return WrapInNameSpace(struct_def.defined_namespace,
|
|
NativeName(Name(struct_def), &struct_def, opts));
|
|
}
|
|
|
|
const std::string& PtrType(const FieldDef* field) {
|
|
auto attr = field ? field->attributes.Lookup("cpp_ptr_type") : nullptr;
|
|
return attr ? attr->constant : opts_.cpp_object_api_pointer_type;
|
|
}
|
|
|
|
const std::string NativeString(const FieldDef* field) {
|
|
auto attr = field ? field->attributes.Lookup("cpp_str_type") : nullptr;
|
|
auto& ret = attr ? attr->constant : opts_.cpp_object_api_string_type;
|
|
if (ret.empty()) {
|
|
return "std::string";
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool FlexibleStringConstructor(const FieldDef* field) {
|
|
auto attr = field != nullptr &&
|
|
(field->attributes.Lookup("cpp_str_flex_ctor") != nullptr);
|
|
auto ret = attr ? attr : opts_.cpp_object_api_string_flexible_constructor;
|
|
return ret && NativeString(field) !=
|
|
"std::string"; // Only for custom string types.
|
|
}
|
|
|
|
std::string GenTypeNativePtr(const std::string& type, const FieldDef* field,
|
|
bool is_constructor) {
|
|
auto& ptr_type = PtrType(field);
|
|
if (ptr_type != "naked") {
|
|
return (ptr_type != "default_ptr_type"
|
|
? ptr_type
|
|
: opts_.cpp_object_api_pointer_type) +
|
|
"<" + type + ">";
|
|
} else if (is_constructor) {
|
|
return "";
|
|
} else {
|
|
return type + " *";
|
|
}
|
|
}
|
|
|
|
std::string GenPtrGet(const FieldDef& field) {
|
|
auto cpp_ptr_type_get = field.attributes.Lookup("cpp_ptr_type_get");
|
|
if (cpp_ptr_type_get) return cpp_ptr_type_get->constant;
|
|
auto& ptr_type = PtrType(&field);
|
|
return ptr_type == "naked" ? "" : ".get()";
|
|
}
|
|
|
|
std::string GenOptionalNull() { return "::flatbuffers::nullopt"; }
|
|
|
|
std::string GenOptionalDecl(const Type& type) {
|
|
return "::flatbuffers::Optional<" + GenTypeBasic(type, true) + ">";
|
|
}
|
|
|
|
std::string GenTypeNative(const Type& type, bool invector,
|
|
const FieldDef& field, bool forcopy = false) {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_STRING: {
|
|
return NativeString(&field);
|
|
}
|
|
case BASE_TYPE_VECTOR64:
|
|
case BASE_TYPE_VECTOR: {
|
|
const auto type_name = GenTypeNative(type.VectorType(), true, field);
|
|
if (type.struct_def &&
|
|
type.struct_def->attributes.Lookup("native_custom_alloc")) {
|
|
auto native_custom_alloc =
|
|
type.struct_def->attributes.Lookup("native_custom_alloc");
|
|
return "std::vector<" + type_name + "," +
|
|
native_custom_alloc->constant + "<" + type_name + ">>";
|
|
} else {
|
|
return "std::vector<" + type_name + ">";
|
|
}
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
auto type_name = WrapInNameSpace(*type.struct_def);
|
|
if (IsStruct(type)) {
|
|
auto native_type = type.struct_def->attributes.Lookup("native_type");
|
|
if (native_type) {
|
|
type_name = native_type->constant;
|
|
}
|
|
if (invector || field.native_inline || forcopy) {
|
|
return type_name;
|
|
} else {
|
|
return GenTypeNativePtr(type_name, &field, false);
|
|
}
|
|
} else {
|
|
const auto nn = WrapNativeNameInNameSpace(*type.struct_def, opts_);
|
|
return (forcopy || field.native_inline)
|
|
? nn
|
|
: GenTypeNativePtr(nn, &field, false);
|
|
}
|
|
}
|
|
case BASE_TYPE_UNION: {
|
|
auto type_name = WrapInNameSpace(*type.enum_def);
|
|
return type_name + "Union";
|
|
}
|
|
default: {
|
|
return field.IsScalarOptional() ? GenOptionalDecl(type)
|
|
: GenTypeBasic(type, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return a C++ type for any type (scalar/pointer) specifically for
|
|
// using a flatbuffer.
|
|
std::string GenTypeGet(const Type& type, const char* afterbasic,
|
|
const char* beforeptr, const char* afterptr,
|
|
bool user_facing_type) {
|
|
if (IsScalar(type.base_type)) {
|
|
return GenTypeBasic(type, user_facing_type) + afterbasic;
|
|
} else if (IsArray(type)) {
|
|
auto element_type = type.VectorType();
|
|
// Check if enum arrays are used in C++ without specifying --scoped-enums
|
|
if (IsEnum(element_type) && !opts_.g_only_fixed_enums) {
|
|
LogCompilerError(
|
|
"--scoped-enums must be enabled to use enum arrays in C++");
|
|
FLATBUFFERS_ASSERT(true);
|
|
}
|
|
return beforeptr +
|
|
(IsScalar(element_type.base_type)
|
|
? GenTypeBasic(element_type, user_facing_type)
|
|
: GenTypePointer(element_type)) +
|
|
afterptr;
|
|
} else {
|
|
return beforeptr + GenTypePointer(type) + afterptr;
|
|
}
|
|
}
|
|
|
|
std::string GenTypeSpan(const Type& type, bool immutable, size_t extent) {
|
|
// Generate "::flatbuffers::span<const U, extent>".
|
|
FLATBUFFERS_ASSERT(IsSeries(type) && "unexpected type");
|
|
auto element_type = type.VectorType();
|
|
std::string text = "::flatbuffers::span<";
|
|
text += immutable ? "const " : "";
|
|
if (IsScalar(element_type.base_type)) {
|
|
text += GenTypeBasic(element_type, IsEnum(element_type));
|
|
} else {
|
|
switch (element_type.base_type) {
|
|
case BASE_TYPE_STRING: {
|
|
text += "char";
|
|
break;
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
FLATBUFFERS_ASSERT(type.struct_def);
|
|
text += WrapInNameSpace(*type.struct_def);
|
|
break;
|
|
}
|
|
default:
|
|
FLATBUFFERS_ASSERT(false && "unexpected element's type");
|
|
break;
|
|
}
|
|
}
|
|
if (extent != dynamic_extent) {
|
|
text += ", ";
|
|
text += NumToString(extent);
|
|
}
|
|
text += "> ";
|
|
return text;
|
|
}
|
|
|
|
std::string GenEnumValDecl(const EnumDef& enum_def,
|
|
const std::string& enum_val) const {
|
|
return opts_.prefixed_enums ? Name(enum_def) + "_" + enum_val : enum_val;
|
|
}
|
|
|
|
std::string GetEnumValUse(const EnumDef& enum_def,
|
|
const EnumVal& enum_val) const {
|
|
if (opts_.scoped_enums) {
|
|
return Name(enum_def) + "::" + Name(enum_val);
|
|
} else if (opts_.prefixed_enums) {
|
|
return Name(enum_def) + "_" + Name(enum_val);
|
|
} else {
|
|
return Name(enum_val);
|
|
}
|
|
}
|
|
|
|
std::string StripUnionType(const std::string& name) {
|
|
return name.substr(0, name.size() - strlen(UnionTypeFieldSuffix()));
|
|
}
|
|
|
|
std::string GetUnionElement(const EnumVal& ev, bool native_type,
|
|
const IDLOptions& opts) {
|
|
if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
|
|
std::string name = ev.union_type.struct_def->name;
|
|
if (native_type) {
|
|
name = NativeName(std::move(name), ev.union_type.struct_def, opts);
|
|
}
|
|
return WrapInNameSpace(ev.union_type.struct_def->defined_namespace, name);
|
|
} else if (IsString(ev.union_type)) {
|
|
return native_type ? "std::string" : "::flatbuffers::String";
|
|
} else {
|
|
FLATBUFFERS_ASSERT(false);
|
|
return Name(ev);
|
|
}
|
|
}
|
|
|
|
// For the initial declaration, we specify the template parameters,
|
|
// including template default arguments.
|
|
std::string UnionVerifyTemplateDecl() { return "template <bool B = false>"; }
|
|
|
|
// For the subsequent definition, we must not redeclare the template default
|
|
// arguments.
|
|
std::string UnionVerifyTemplateDef() { return "template <bool B>"; }
|
|
|
|
// Should be used in conjunction with
|
|
// UnionVerifyTemplateDecl()/UnionVerifyTemplateDef().
|
|
std::string UnionVerifySignature(const EnumDef& enum_def) {
|
|
return "bool Verify" + Name(enum_def) +
|
|
"(::flatbuffers::VerifierTemplate<B> &verifier, " +
|
|
"const void *obj, " + Name(enum_def) + " type)";
|
|
}
|
|
|
|
// Should be used in conjunction with
|
|
// UnionVerifyTemplateDecl()/UnionVerifyTemplateDef().
|
|
std::string UnionVectorVerifySignature(const EnumDef& enum_def) {
|
|
const std::string name = Name(enum_def);
|
|
const std::string& type =
|
|
opts_.scoped_enums ? name
|
|
: GenTypeBasic(enum_def.underlying_type, false);
|
|
return "bool Verify" + name + "Vector" +
|
|
"(::flatbuffers::VerifierTemplate<B> &verifier, " +
|
|
"const ::flatbuffers::Vector<::flatbuffers::Offset<void>> "
|
|
"*values, " +
|
|
"const ::flatbuffers::Vector<" + type + "> *types)";
|
|
}
|
|
|
|
std::string UnionUnPackSignature(const EnumDef& enum_def, bool inclass) {
|
|
return (inclass ? "static " : "") + std::string("void *") +
|
|
(inclass ? "" : Name(enum_def) + "Union::") +
|
|
"UnPack(const void *obj, " + Name(enum_def) +
|
|
" type, const ::flatbuffers::resolver_function_t *resolver)";
|
|
}
|
|
|
|
std::string UnionPackSignature(const EnumDef& enum_def, bool inclass) {
|
|
return "::flatbuffers::Offset<void> " +
|
|
(inclass ? "" : Name(enum_def) + "Union::") + "Pack(" +
|
|
GetBuilder() + " &_fbb, " +
|
|
"const ::flatbuffers::rehasher_function_t *_rehasher" +
|
|
(inclass ? " = nullptr" : "") + ") const";
|
|
}
|
|
|
|
std::string TableCreateSignature(const StructDef& struct_def, bool predecl,
|
|
const IDLOptions& opts) {
|
|
return "::flatbuffers::Offset<" + Name(struct_def) + "> Create" +
|
|
Name(struct_def) + "(" + GetBuilder() + " &_fbb, const " +
|
|
NativeName(Name(struct_def), &struct_def, opts) +
|
|
" *_o, const ::flatbuffers::rehasher_function_t *_rehasher" +
|
|
(predecl ? " = nullptr" : "") + ")";
|
|
}
|
|
|
|
std::string TablePackSignature(const StructDef& struct_def, bool inclass,
|
|
const IDLOptions& opts) {
|
|
return std::string(inclass ? "static " : "") + "::flatbuffers::Offset<" +
|
|
Name(struct_def) + "> " + (inclass ? "" : Name(struct_def) + "::") +
|
|
"Pack(" + GetBuilder() + " &_fbb, " + "const " +
|
|
NativeName(Name(struct_def), &struct_def, opts) + "* _o, " +
|
|
"const ::flatbuffers::rehasher_function_t *_rehasher" +
|
|
(inclass ? " = nullptr" : "") + ")";
|
|
}
|
|
|
|
std::string TableUnPackSignature(const StructDef& struct_def, bool inclass,
|
|
const IDLOptions& opts) {
|
|
return NativeName(Name(struct_def), &struct_def, opts) + " *" +
|
|
(inclass ? "" : Name(struct_def) + "::") +
|
|
"UnPack(const ::flatbuffers::resolver_function_t *_resolver" +
|
|
(inclass ? " = nullptr" : "") + ") const";
|
|
}
|
|
|
|
std::string TableUnPackToSignature(const StructDef& struct_def, bool inclass,
|
|
const IDLOptions& opts) {
|
|
return "void " + (inclass ? "" : Name(struct_def) + "::") + "UnPackTo(" +
|
|
NativeName(Name(struct_def), &struct_def, opts) + " *" +
|
|
"_o, const ::flatbuffers::resolver_function_t *_resolver" +
|
|
(inclass ? " = nullptr" : "") + ") const";
|
|
}
|
|
|
|
void GenMiniReflectPre(const StructDef* struct_def) {
|
|
code_.SetValue("NAME", struct_def->name);
|
|
code_ += "inline const ::flatbuffers::TypeTable *{{NAME}}TypeTable();";
|
|
code_ += "";
|
|
}
|
|
|
|
void GenMiniReflect(const StructDef* struct_def, const EnumDef* enum_def) {
|
|
code_.SetValue("NAME", struct_def ? struct_def->name : enum_def->name);
|
|
code_.SetValue("SEQ_TYPE",
|
|
struct_def ? (struct_def->fixed ? "ST_STRUCT" : "ST_TABLE")
|
|
: (enum_def->is_union ? "ST_UNION" : "ST_ENUM"));
|
|
auto num_fields =
|
|
struct_def ? struct_def->fields.vec.size() : enum_def->size();
|
|
code_.SetValue("NUM_FIELDS", NumToString(num_fields));
|
|
std::vector<std::string> names;
|
|
std::vector<Type> types;
|
|
|
|
if (struct_def) {
|
|
for (const auto& field : struct_def->fields.vec) {
|
|
names.push_back(Name(*field));
|
|
types.push_back(field->value.type);
|
|
}
|
|
} else {
|
|
for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
|
|
++it) {
|
|
const auto& ev = **it;
|
|
names.push_back(Name(ev));
|
|
types.push_back(enum_def->is_union ? ev.union_type
|
|
: Type(enum_def->underlying_type));
|
|
}
|
|
}
|
|
std::string ts;
|
|
std::vector<std::string> type_refs;
|
|
std::vector<uint16_t> array_sizes;
|
|
for (auto& type : types) {
|
|
if (!ts.empty()) ts += ",\n ";
|
|
auto is_vector = IsVector(type);
|
|
auto is_array = IsArray(type);
|
|
auto bt = is_vector || is_array ? type.element : type.base_type;
|
|
auto et = IsScalar(bt) || bt == BASE_TYPE_STRING
|
|
? bt - BASE_TYPE_UTYPE + ET_UTYPE
|
|
: ET_SEQUENCE;
|
|
int ref_idx = -1;
|
|
std::string ref_name = type.struct_def ? WrapInNameSpace(*type.struct_def)
|
|
: type.enum_def ? WrapInNameSpace(*type.enum_def)
|
|
: "";
|
|
if (!ref_name.empty()) {
|
|
auto rit = type_refs.begin();
|
|
for (; rit != type_refs.end(); ++rit) {
|
|
if (*rit == ref_name) {
|
|
ref_idx = static_cast<int>(rit - type_refs.begin());
|
|
break;
|
|
}
|
|
}
|
|
if (rit == type_refs.end()) {
|
|
ref_idx = static_cast<int>(type_refs.size());
|
|
type_refs.push_back(ref_name);
|
|
}
|
|
}
|
|
if (is_array) {
|
|
array_sizes.push_back(type.fixed_length);
|
|
}
|
|
ts += "{ ::flatbuffers::" + std::string(ElementaryTypeNames()[et]) +
|
|
", " + NumToString(is_vector || is_array) + ", " +
|
|
NumToString(ref_idx) + " }";
|
|
}
|
|
std::string rs;
|
|
for (auto& type_ref : type_refs) {
|
|
if (!rs.empty()) rs += ",\n ";
|
|
rs += type_ref + "TypeTable";
|
|
}
|
|
std::string as;
|
|
for (auto& array_size : array_sizes) {
|
|
as += NumToString(array_size);
|
|
as += ", ";
|
|
}
|
|
std::string ns;
|
|
for (auto& name : names) {
|
|
if (!ns.empty()) ns += ",\n ";
|
|
ns += "\"" + name + "\"";
|
|
}
|
|
std::string vs;
|
|
const auto consecutive_enum_from_zero =
|
|
enum_def && enum_def->MinValue()->IsZero() &&
|
|
((enum_def->size() - 1) == enum_def->Distance());
|
|
if (enum_def && !consecutive_enum_from_zero) {
|
|
for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
|
|
++it) {
|
|
const auto& ev = **it;
|
|
if (!vs.empty()) vs += ", ";
|
|
vs += NumToStringCpp(enum_def->ToString(ev),
|
|
enum_def->underlying_type.base_type);
|
|
}
|
|
} else if (struct_def && struct_def->fixed) {
|
|
for (const auto field : struct_def->fields.vec) {
|
|
vs += NumToString(field->value.offset);
|
|
vs += ", ";
|
|
}
|
|
vs += NumToString(struct_def->bytesize);
|
|
}
|
|
code_.SetValue("TYPES", ts);
|
|
code_.SetValue("REFS", rs);
|
|
code_.SetValue("ARRAYSIZES", as);
|
|
code_.SetValue("NAMES", ns);
|
|
code_.SetValue("VALUES", vs);
|
|
code_ += "inline const ::flatbuffers::TypeTable *{{NAME}}TypeTable() {";
|
|
if (num_fields) {
|
|
code_ += " static const ::flatbuffers::TypeCode type_codes[] = {";
|
|
code_ += " {{TYPES}}";
|
|
code_ += " };";
|
|
}
|
|
if (!type_refs.empty()) {
|
|
code_ += " static const ::flatbuffers::TypeFunction type_refs[] = {";
|
|
code_ += " {{REFS}}";
|
|
code_ += " };";
|
|
}
|
|
if (!as.empty()) {
|
|
code_ += " static const int16_t array_sizes[] = { {{ARRAYSIZES}} };";
|
|
}
|
|
if (!vs.empty()) {
|
|
// Problem with uint64_t values greater than 9223372036854775807ULL.
|
|
code_ += " static const int64_t values[] = { {{VALUES}} };";
|
|
}
|
|
auto has_names =
|
|
num_fields && opts_.mini_reflect == IDLOptions::kTypesAndNames;
|
|
if (has_names) {
|
|
code_ += " static const char * const names[] = {";
|
|
code_ += " {{NAMES}}";
|
|
code_ += " };";
|
|
}
|
|
code_ += " static const ::flatbuffers::TypeTable tt = {";
|
|
code_ += std::string(" ::flatbuffers::{{SEQ_TYPE}}, {{NUM_FIELDS}}, ") +
|
|
(num_fields ? "type_codes, " : "nullptr, ") +
|
|
(!type_refs.empty() ? "type_refs, " : "nullptr, ") +
|
|
(!as.empty() ? "array_sizes, " : "nullptr, ") +
|
|
(!vs.empty() ? "values, " : "nullptr, ") +
|
|
(has_names ? "names" : "nullptr");
|
|
code_ += " };";
|
|
code_ += " return &tt;";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
// Generate an enum declaration,
|
|
// an enum string lookup table,
|
|
// and an enum array of values
|
|
|
|
void GenEnum(const EnumDef& enum_def) {
|
|
code_.SetValue("ENUM_NAME", Name(enum_def));
|
|
code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false));
|
|
|
|
GenComment(enum_def.doc_comment);
|
|
code_ +=
|
|
(opts_.scoped_enums ? "enum class " : "enum ") + Name(enum_def) + "\\";
|
|
if (opts_.g_only_fixed_enums) {
|
|
code_ += " : {{BASE_TYPE}}\\";
|
|
}
|
|
code_ += " {";
|
|
|
|
code_.SetValue("SEP", ",");
|
|
auto add_sep = false;
|
|
for (const auto ev : enum_def.Vals()) {
|
|
if (add_sep) code_ += "{{SEP}}";
|
|
GenComment(ev->doc_comment, " ");
|
|
code_.SetValue("KEY", GenEnumValDecl(enum_def, Name(*ev)));
|
|
code_.SetValue("VALUE",
|
|
NumToStringCpp(enum_def.ToString(*ev),
|
|
enum_def.underlying_type.base_type));
|
|
code_ += " {{KEY}} = {{VALUE}}\\";
|
|
add_sep = true;
|
|
}
|
|
if (opts_.cpp_minify_enums) {
|
|
code_ += "";
|
|
code_ += "};";
|
|
return;
|
|
}
|
|
const EnumVal* minv = enum_def.MinValue();
|
|
const EnumVal* maxv = enum_def.MaxValue();
|
|
|
|
if (opts_.scoped_enums || opts_.prefixed_enums) {
|
|
FLATBUFFERS_ASSERT(minv && maxv);
|
|
|
|
code_.SetValue("SEP", ",\n");
|
|
|
|
// MIN & MAX are useless for bit_flags
|
|
if (enum_def.attributes.Lookup("bit_flags")) {
|
|
code_.SetValue("KEY", GenEnumValDecl(enum_def, "NONE"));
|
|
code_.SetValue("VALUE", "0");
|
|
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
|
|
|
|
code_.SetValue("KEY", GenEnumValDecl(enum_def, "ANY"));
|
|
code_.SetValue("VALUE",
|
|
NumToStringCpp(enum_def.AllFlags(),
|
|
enum_def.underlying_type.base_type));
|
|
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
|
|
} else if (opts_.emit_min_max_enum_values) {
|
|
code_.SetValue("KEY", GenEnumValDecl(enum_def, "MIN"));
|
|
code_.SetValue("VALUE", GenEnumValDecl(enum_def, Name(*minv)));
|
|
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
|
|
|
|
code_.SetValue("KEY", GenEnumValDecl(enum_def, "MAX"));
|
|
code_.SetValue("VALUE", GenEnumValDecl(enum_def, Name(*maxv)));
|
|
code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
|
|
}
|
|
}
|
|
code_ += "";
|
|
code_ += "};";
|
|
|
|
if (opts_.scoped_enums && enum_def.attributes.Lookup("bit_flags")) {
|
|
code_ +=
|
|
"FLATBUFFERS_DEFINE_BITMASK_OPERATORS({{ENUM_NAME}}, {{BASE_TYPE}})";
|
|
}
|
|
code_ += "";
|
|
GenEnumArray(enum_def);
|
|
GenEnumStringTable(enum_def);
|
|
|
|
// Generate type traits for unions to map from a type to union enum value.
|
|
if (enum_def.is_union && !enum_def.uses_multiple_type_instances) {
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
|
|
++it) {
|
|
const auto& ev = **it;
|
|
|
|
if (it == enum_def.Vals().begin()) {
|
|
code_ += "template<typename T> struct {{ENUM_NAME}}Traits {";
|
|
} else {
|
|
auto name = GetUnionElement(ev, false, opts_);
|
|
code_ += "template<> struct {{ENUM_NAME}}Traits<" + name + "> {";
|
|
}
|
|
|
|
auto value = GetEnumValUse(enum_def, ev);
|
|
code_ += " static const {{ENUM_NAME}} enum_value = " + value + ";";
|
|
code_ += "};";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
GenEnumObjectBasedAPI(enum_def);
|
|
|
|
if (enum_def.is_union) {
|
|
code_ += UnionVerifyTemplateDecl();
|
|
code_ += UnionVerifySignature(enum_def) + ";";
|
|
code_ += UnionVerifyTemplateDecl();
|
|
code_ += UnionVectorVerifySignature(enum_def) + ";";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
// Generate a union type and a trait type for it.
|
|
void GenEnumObjectBasedAPI(const EnumDef& enum_def) {
|
|
if (!(opts_.generate_object_based_api && enum_def.is_union)) {
|
|
return;
|
|
}
|
|
code_.SetValue("NAME", Name(enum_def));
|
|
FLATBUFFERS_ASSERT(enum_def.Lookup("NONE"));
|
|
code_.SetValue("NONE", GetEnumValUse(enum_def, *enum_def.Lookup("NONE")));
|
|
|
|
if (!enum_def.uses_multiple_type_instances) {
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
|
|
++it) {
|
|
const auto& ev = **it;
|
|
|
|
if (it == enum_def.Vals().begin()) {
|
|
code_ += "template<typename T> struct {{NAME}}UnionTraits {";
|
|
} else {
|
|
auto name = GetUnionElement(ev, true, opts_);
|
|
code_ += "template<> struct {{NAME}}UnionTraits<" + name + "> {";
|
|
}
|
|
|
|
auto value = GetEnumValUse(enum_def, ev);
|
|
code_ += " static const {{ENUM_NAME}} enum_value = " + value + ";";
|
|
code_ += "};";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
code_ += "struct {{NAME}}Union {";
|
|
code_ += " {{NAME}} type;";
|
|
code_ += " void *value;";
|
|
code_ += "";
|
|
code_ += " {{NAME}}Union() : type({{NONE}}), value(nullptr) {}";
|
|
code_ += " {{NAME}}Union({{NAME}}Union&& u) FLATBUFFERS_NOEXCEPT :";
|
|
code_ += " type({{NONE}}), value(nullptr)";
|
|
code_ += " { std::swap(type, u.type); std::swap(value, u.value); }";
|
|
code_ += " {{NAME}}Union(const {{NAME}}Union &);";
|
|
code_ += " {{NAME}}Union &operator=(const {{NAME}}Union &u)";
|
|
code_ +=
|
|
" { {{NAME}}Union t(u); std::swap(type, t.type); std::swap(value, "
|
|
"t.value); return *this; }";
|
|
code_ +=
|
|
" {{NAME}}Union &operator=({{NAME}}Union &&u) FLATBUFFERS_NOEXCEPT";
|
|
code_ +=
|
|
" { std::swap(type, u.type); std::swap(value, u.value); return "
|
|
"*this; }";
|
|
code_ += " ~{{NAME}}Union() { Reset(); }";
|
|
code_ += "";
|
|
code_ += " void Reset();";
|
|
code_ += "";
|
|
if (!enum_def.uses_multiple_type_instances) {
|
|
code_ += " template <typename T>";
|
|
code_ += " void Set(T&& val) {";
|
|
code_ += " typedef typename std::remove_reference<T>::type RT;";
|
|
code_ += " Reset();";
|
|
code_ += " type = {{NAME}}UnionTraits<RT>::enum_value;";
|
|
code_ += " if (type != {{NONE}}) {";
|
|
code_ += " value = new RT(std::forward<T>(val));";
|
|
code_ += " }";
|
|
code_ += " }";
|
|
code_ += "";
|
|
}
|
|
code_ += " " + UnionUnPackSignature(enum_def, true) + ";";
|
|
code_ += " " + UnionPackSignature(enum_def, true) + ";";
|
|
code_ += "";
|
|
|
|
for (const auto ev : enum_def.Vals()) {
|
|
if (ev->IsZero()) {
|
|
continue;
|
|
}
|
|
|
|
const auto native_type = GetUnionElement(*ev, true, opts_);
|
|
code_.SetValue("NATIVE_TYPE", native_type);
|
|
code_.SetValue("NATIVE_NAME", Name(*ev));
|
|
code_.SetValue("NATIVE_ID", GetEnumValUse(enum_def, *ev));
|
|
|
|
code_ += " {{NATIVE_TYPE}} *As{{NATIVE_NAME}}() {";
|
|
code_ += " return type == {{NATIVE_ID}} ?";
|
|
code_ += " reinterpret_cast<{{NATIVE_TYPE}} *>(value) : nullptr;";
|
|
code_ += " }";
|
|
|
|
code_ += " const {{NATIVE_TYPE}} *As{{NATIVE_NAME}}() const {";
|
|
code_ += " return type == {{NATIVE_ID}} ?";
|
|
code_ +=
|
|
" reinterpret_cast<const {{NATIVE_TYPE}} *>(value) : nullptr;";
|
|
code_ += " }";
|
|
}
|
|
code_ += "};";
|
|
code_ += "";
|
|
|
|
GenEnumEquals(enum_def);
|
|
}
|
|
|
|
void GenEnumEquals(const EnumDef& enum_def) {
|
|
if (opts_.gen_compare) {
|
|
code_ += "";
|
|
code_ +=
|
|
"inline bool operator==(const {{NAME}}Union &lhs, const "
|
|
"{{NAME}}Union &rhs) {";
|
|
code_ += " if (lhs.type != rhs.type) return false;";
|
|
code_ += " switch (lhs.type) {";
|
|
|
|
for (const auto& ev : enum_def.Vals()) {
|
|
code_.SetValue("NATIVE_ID", GetEnumValUse(enum_def, *ev));
|
|
if (ev->IsNonZero()) {
|
|
const auto native_type = GetUnionElement(*ev, true, opts_);
|
|
code_.SetValue("NATIVE_TYPE", native_type);
|
|
code_ += " case {{NATIVE_ID}}: {";
|
|
code_ +=
|
|
" return *(reinterpret_cast<const {{NATIVE_TYPE}} "
|
|
"*>(lhs.value)) ==";
|
|
code_ +=
|
|
" *(reinterpret_cast<const {{NATIVE_TYPE}} "
|
|
"*>(rhs.value));";
|
|
code_ += " }";
|
|
} else {
|
|
code_ += " case {{NATIVE_ID}}: {";
|
|
code_ += " return true;"; // "NONE" enum value.
|
|
code_ += " }";
|
|
}
|
|
}
|
|
code_ += " default: {";
|
|
code_ += " return false;";
|
|
code_ += " }";
|
|
code_ += " }";
|
|
code_ += "}";
|
|
|
|
code_ += "";
|
|
code_ +=
|
|
"inline bool operator!=(const {{NAME}}Union &lhs, const "
|
|
"{{NAME}}Union &rhs) {";
|
|
code_ += " return !(lhs == rhs);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
// Generate an array of all enumeration values
|
|
void GenEnumArray(const EnumDef& enum_def) {
|
|
auto num_fields = NumToString(enum_def.size());
|
|
code_ += "inline const {{ENUM_NAME}} (&EnumValues{{ENUM_NAME}}())[" +
|
|
num_fields + "] {";
|
|
code_ += " static const {{ENUM_NAME}} values[] = {";
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
|
|
const auto& ev = **it;
|
|
auto value = GetEnumValUse(enum_def, ev);
|
|
auto suffix = *it != enum_def.Vals().back() ? "," : "";
|
|
code_ += " " + value + suffix;
|
|
}
|
|
code_ += " };";
|
|
code_ += " return values;";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
// Generate a string table for enum values.
|
|
// Problem is, if values are very sparse that could generate huge tables.
|
|
// Ideally in that case we generate a map lookup instead, but for the moment
|
|
// we simply don't output a table at all.
|
|
void GenEnumStringTable(const EnumDef& enum_def) {
|
|
auto range = enum_def.Distance();
|
|
// Average distance between values above which we consider a table
|
|
// "too sparse". Change at will.
|
|
static const uint64_t kMaxSparseness = 5;
|
|
if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
|
|
code_ += "inline const char * const *EnumNames{{ENUM_NAME}}() {";
|
|
code_ += " static const char * const names[" +
|
|
NumToString(range + 1 + 1) + "] = {";
|
|
|
|
auto val = enum_def.Vals().front();
|
|
for (const auto& enum_value : enum_def.Vals()) {
|
|
for (auto k = enum_def.Distance(val, enum_value); k > 1; --k) {
|
|
code_ += " \"\",";
|
|
}
|
|
val = enum_value;
|
|
code_ += " \"" + Name(*enum_value) + "\",";
|
|
}
|
|
code_ += " nullptr";
|
|
code_ += " };";
|
|
|
|
code_ += " return names;";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "inline const char *EnumName{{ENUM_NAME}}({{ENUM_NAME}} e) {";
|
|
|
|
code_ += " if (::flatbuffers::IsOutRange(e, " +
|
|
GetEnumValUse(enum_def, *enum_def.MinValue()) + ", " +
|
|
GetEnumValUse(enum_def, *enum_def.MaxValue()) +
|
|
")) return \"\";";
|
|
|
|
code_ += " const size_t index = static_cast<size_t>(e)\\";
|
|
if (enum_def.MinValue()->IsNonZero()) {
|
|
auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
|
|
code_ += " - static_cast<size_t>(" + vals + ")\\";
|
|
}
|
|
code_ += ";";
|
|
|
|
code_ += " return EnumNames{{ENUM_NAME}}()[index];";
|
|
code_ += "}";
|
|
code_ += "";
|
|
} else {
|
|
code_ += "inline const char *EnumName{{ENUM_NAME}}({{ENUM_NAME}} e) {";
|
|
code_ += " switch (e) {";
|
|
for (const auto& ev : enum_def.Vals()) {
|
|
code_ += " case " + GetEnumValUse(enum_def, *ev) + ": return \"" +
|
|
Name(*ev) + "\";";
|
|
}
|
|
code_ += " default: return \"\";";
|
|
code_ += " }";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
void GenUnionPost(const EnumDef& enum_def) {
|
|
// Generate a verifier function for this union that can be called by the
|
|
// table verifier functions. It uses a switch case to select a specific
|
|
// verifier function to call, this should be safe even if the union type
|
|
// has been corrupted, since the verifiers will simply fail when called
|
|
// on the wrong type.
|
|
code_.SetValue("ENUM_NAME", Name(enum_def));
|
|
|
|
code_ += UnionVerifyTemplateDef();
|
|
code_ += "inline " + UnionVerifySignature(enum_def) + " {";
|
|
code_ += " switch (type) {";
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
|
|
const auto& ev = **it;
|
|
code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
|
|
|
|
if (ev.IsNonZero()) {
|
|
code_.SetValue("TYPE", GetUnionElement(ev, false, opts_));
|
|
code_ += " case {{LABEL}}: {";
|
|
auto getptr =
|
|
" auto ptr = reinterpret_cast<const {{TYPE}} *>(obj);";
|
|
if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
|
|
if (ev.union_type.struct_def->fixed) {
|
|
code_.SetValue("ALIGN",
|
|
NumToString(ev.union_type.struct_def->minalign));
|
|
code_ +=
|
|
" return verifier.template VerifyField<{{TYPE}}>("
|
|
"static_cast<const uint8_t *>(obj), 0, {{ALIGN}});";
|
|
} else {
|
|
code_ += getptr;
|
|
code_ += " return verifier.VerifyTable(ptr);";
|
|
}
|
|
} else if (IsString(ev.union_type)) {
|
|
code_ += getptr;
|
|
code_ += " return verifier.VerifyString(ptr);";
|
|
} else {
|
|
FLATBUFFERS_ASSERT(false);
|
|
}
|
|
code_ += " }";
|
|
} else {
|
|
code_ += " case {{LABEL}}: {";
|
|
code_ += " return true;"; // "NONE" enum value.
|
|
code_ += " }";
|
|
}
|
|
}
|
|
code_ += " default: return true;"; // unknown values are OK.
|
|
code_ += " }";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += UnionVerifyTemplateDef();
|
|
code_ += "inline " + UnionVectorVerifySignature(enum_def) + " {";
|
|
code_ += " if (!values || !types) return !values && !types;";
|
|
code_ += " if (values->size() != types->size()) return false;";
|
|
code_ +=
|
|
" for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {";
|
|
code_ += " if (!Verify" + Name(enum_def) + "(";
|
|
code_ += " verifier, values->Get(i), types->GetEnum<" +
|
|
Name(enum_def) + ">(i))) {";
|
|
code_ += " return false;";
|
|
code_ += " }";
|
|
code_ += " }";
|
|
code_ += " return true;";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
if (opts_.generate_object_based_api) {
|
|
// Generate union Unpack() and Pack() functions.
|
|
code_ += "inline " + UnionUnPackSignature(enum_def, false) + " {";
|
|
code_ += " (void)resolver;";
|
|
code_ += " switch (type) {";
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
|
|
++it) {
|
|
const auto& ev = **it;
|
|
if (ev.IsZero()) {
|
|
continue;
|
|
}
|
|
|
|
code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
|
|
code_.SetValue("TYPE", GetUnionElement(ev, false, opts_));
|
|
code_ += " case {{LABEL}}: {";
|
|
code_ += " auto ptr = reinterpret_cast<const {{TYPE}} *>(obj);";
|
|
if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
|
|
if (ev.union_type.struct_def->fixed) {
|
|
code_ += " return new " +
|
|
WrapInNameSpace(*ev.union_type.struct_def) + "(*ptr);";
|
|
} else {
|
|
code_ += " return ptr->UnPack(resolver);";
|
|
}
|
|
} else if (IsString(ev.union_type)) {
|
|
code_ += " return new std::string(ptr->c_str(), ptr->size());";
|
|
} else {
|
|
FLATBUFFERS_ASSERT(false);
|
|
}
|
|
code_ += " }";
|
|
}
|
|
code_ += " default: return nullptr;";
|
|
code_ += " }";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
code_ += "inline " + UnionPackSignature(enum_def, false) + " {";
|
|
code_ += " (void)_rehasher;";
|
|
code_ += " switch (type) {";
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
|
|
++it) {
|
|
auto& ev = **it;
|
|
if (ev.IsZero()) {
|
|
continue;
|
|
}
|
|
|
|
code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
|
|
code_.SetValue("TYPE", GetUnionElement(ev, true, opts_));
|
|
code_ += " case {{LABEL}}: {";
|
|
code_ += " auto ptr = reinterpret_cast<const {{TYPE}} *>(value);";
|
|
if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
|
|
if (ev.union_type.struct_def->fixed) {
|
|
code_ += " return _fbb.CreateStruct(*ptr).Union();";
|
|
} else {
|
|
code_.SetValue("NAME", ev.union_type.struct_def->name);
|
|
code_ +=
|
|
" return Create{{NAME}}(_fbb, ptr, _rehasher).Union();";
|
|
}
|
|
} else if (IsString(ev.union_type)) {
|
|
code_ += " return _fbb.CreateString(*ptr).Union();";
|
|
} else {
|
|
FLATBUFFERS_ASSERT(false);
|
|
}
|
|
code_ += " }";
|
|
}
|
|
code_ += " default: return 0;";
|
|
code_ += " }";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
// Union copy constructor
|
|
code_ +=
|
|
"inline {{ENUM_NAME}}Union::{{ENUM_NAME}}Union(const "
|
|
"{{ENUM_NAME}}Union &u) : type(u.type), value(nullptr) {";
|
|
code_ += " switch (type) {";
|
|
for (const auto& ev : enum_def.Vals()) {
|
|
if (ev->IsZero()) {
|
|
continue;
|
|
}
|
|
code_.SetValue("LABEL", GetEnumValUse(enum_def, *ev));
|
|
code_.SetValue("TYPE", GetUnionElement(*ev, true, opts_));
|
|
code_ += " case {{LABEL}}: {";
|
|
bool copyable = true;
|
|
if (opts_.g_cpp_std < cpp::CPP_STD_11 &&
|
|
ev->union_type.base_type == BASE_TYPE_STRUCT &&
|
|
!ev->union_type.struct_def->fixed) {
|
|
// Don't generate code to copy if table is not copyable.
|
|
// TODO(wvo): make tables copyable instead.
|
|
for (const auto& field : ev->union_type.struct_def->fields.vec) {
|
|
if (!field->deprecated && field->value.type.struct_def &&
|
|
!field->native_inline) {
|
|
copyable = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (copyable) {
|
|
code_ +=
|
|
" value = new {{TYPE}}(*reinterpret_cast<{{TYPE}} *>"
|
|
"(u.value));";
|
|
} else {
|
|
code_ +=
|
|
" FLATBUFFERS_ASSERT(false); // {{TYPE}} not copyable.";
|
|
}
|
|
code_ += " break;";
|
|
code_ += " }";
|
|
}
|
|
code_ += " default:";
|
|
code_ += " break;";
|
|
code_ += " }";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
// Union Reset() function.
|
|
FLATBUFFERS_ASSERT(enum_def.Lookup("NONE"));
|
|
code_.SetValue("NONE", GetEnumValUse(enum_def, *enum_def.Lookup("NONE")));
|
|
|
|
code_ += "inline void {{ENUM_NAME}}Union::Reset() {";
|
|
code_ += " switch (type) {";
|
|
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
|
|
++it) {
|
|
const auto& ev = **it;
|
|
if (ev.IsZero()) {
|
|
continue;
|
|
}
|
|
code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
|
|
code_.SetValue("TYPE", GetUnionElement(ev, true, opts_));
|
|
code_ += " case {{LABEL}}: {";
|
|
code_ += " auto ptr = reinterpret_cast<{{TYPE}} *>(value);";
|
|
code_ += " delete ptr;";
|
|
code_ += " break;";
|
|
code_ += " }";
|
|
}
|
|
code_ += " default: break;";
|
|
code_ += " }";
|
|
code_ += " value = nullptr;";
|
|
code_ += " type = {{NONE}};";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
// Generates a value with optionally a cast applied if the field has a
|
|
// different underlying type from its interface type (currently only the
|
|
// case for enums. "from" specify the direction, true meaning from the
|
|
// underlying type to the interface type.
|
|
std::string GenUnderlyingCast(const FieldDef& field, bool from,
|
|
const std::string& val) {
|
|
if (from && field.value.type.base_type == BASE_TYPE_BOOL) {
|
|
return val + " != 0";
|
|
} else if ((field.value.type.enum_def &&
|
|
IsScalar(field.value.type.base_type)) ||
|
|
field.value.type.base_type == BASE_TYPE_BOOL) {
|
|
return "static_cast<" + GenTypeBasic(field.value.type, from) + ">(" +
|
|
val + ")";
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
|
|
std::string GenFieldOffsetName(const FieldDef& field) {
|
|
std::string uname = Name(field);
|
|
std::transform(uname.begin(), uname.end(), uname.begin(), CharToUpper);
|
|
return "VT_" + uname;
|
|
}
|
|
|
|
void GenFullyQualifiedNameGetter(const StructDef& struct_def,
|
|
const std::string& name) {
|
|
if (!opts_.generate_name_strings) {
|
|
return;
|
|
}
|
|
auto fullname = struct_def.defined_namespace->GetFullyQualifiedName(name);
|
|
code_.SetValue("NAME", fullname);
|
|
code_.SetValue("CONSTEXPR", "FLATBUFFERS_CONSTEXPR_CPP11");
|
|
code_ += " static {{CONSTEXPR}} const char *GetFullyQualifiedName() {";
|
|
code_ += " return \"{{NAME}}\";";
|
|
code_ += " }";
|
|
}
|
|
|
|
std::string GenDefaultConstant(const FieldDef& field) {
|
|
if (IsFloat(field.value.type.base_type))
|
|
return float_const_gen_.GenFloatConstant(field);
|
|
else
|
|
return NumToStringCpp(field.value.constant, field.value.type.base_type);
|
|
}
|
|
|
|
std::string GetDefaultScalarValue(const FieldDef& field, bool is_ctor) {
|
|
const auto& type = field.value.type;
|
|
if (field.IsScalarOptional()) {
|
|
return GenOptionalNull();
|
|
} else if (type.enum_def && IsScalar(type.base_type)) {
|
|
auto ev = type.enum_def->FindByValue(field.value.constant);
|
|
if (ev) {
|
|
return WrapInNameSpace(type.enum_def->defined_namespace,
|
|
GetEnumValUse(*type.enum_def, *ev));
|
|
} else {
|
|
return GenUnderlyingCast(
|
|
field, true, NumToStringCpp(field.value.constant, type.base_type));
|
|
}
|
|
} else if (type.base_type == BASE_TYPE_BOOL) {
|
|
return field.value.constant == "0" ? "false" : "true";
|
|
} else if (field.attributes.Lookup("cpp_type")) {
|
|
if (is_ctor) {
|
|
if (PtrType(&field) == "naked") {
|
|
return "nullptr";
|
|
} else {
|
|
return "";
|
|
}
|
|
} else {
|
|
return "0";
|
|
}
|
|
} else if (IsVector(type) && field.value.constant == "[]") {
|
|
return "0";
|
|
} else if (IsString(type) && field.value.constant != "0") {
|
|
return "0";
|
|
} else if (IsStruct(type) && (field.value.constant == "0")) {
|
|
return "nullptr";
|
|
} else {
|
|
return GenDefaultConstant(field);
|
|
}
|
|
}
|
|
|
|
void GenParam(const FieldDef& field, bool direct, const char* prefix) {
|
|
code_.SetValue("PRE", prefix);
|
|
code_.SetValue("PARAM_NAME", Name(field));
|
|
if (direct && IsString(field.value.type)) {
|
|
code_.SetValue("PARAM_TYPE", "const char *");
|
|
code_.SetValue("PARAM_VALUE", "nullptr");
|
|
} else if (direct && IsVector(field.value.type)) {
|
|
const auto vtype = field.value.type.VectorType();
|
|
std::string type;
|
|
if (IsStruct(vtype)) {
|
|
type = WrapInNameSpace(*vtype.struct_def);
|
|
} else {
|
|
type = GenTypeWire(vtype, "", VectorElementUserFacing(vtype),
|
|
field.offset64);
|
|
}
|
|
if (TypeHasKey(vtype)) {
|
|
code_.SetValue("PARAM_TYPE", "std::vector<" + type + "> *");
|
|
} else {
|
|
code_.SetValue("PARAM_TYPE", "const std::vector<" + type + "> *");
|
|
}
|
|
code_.SetValue("PARAM_VALUE", "nullptr");
|
|
} else {
|
|
const auto& type = field.value.type;
|
|
code_.SetValue("PARAM_VALUE", GetDefaultScalarValue(field, false));
|
|
if (field.IsScalarOptional())
|
|
code_.SetValue("PARAM_TYPE", GenOptionalDecl(type) + " ");
|
|
else
|
|
code_.SetValue("PARAM_TYPE",
|
|
GenTypeWire(type, " ", true, field.offset64));
|
|
}
|
|
code_ += "{{PRE}}{{PARAM_TYPE}}{{PARAM_NAME}} = {{PARAM_VALUE}}\\";
|
|
}
|
|
|
|
// Generate a member, including a default value for scalars and raw pointers.
|
|
void GenMember(const FieldDef& field) {
|
|
if (!field.deprecated && // Deprecated fields won't be accessible.
|
|
field.value.type.base_type != BASE_TYPE_UTYPE &&
|
|
(!IsVector(field.value.type) ||
|
|
field.value.type.element != BASE_TYPE_UTYPE)) {
|
|
auto type = GenTypeNative(field.value.type, false, field);
|
|
auto cpp_type = field.attributes.Lookup("cpp_type");
|
|
const std::string& full_type =
|
|
(cpp_type
|
|
? (IsVector(field.value.type)
|
|
? "std::vector<" +
|
|
GenTypeNativePtr(cpp_type->constant, &field,
|
|
false) +
|
|
"> "
|
|
: GenTypeNativePtr(cpp_type->constant, &field, false))
|
|
: type + " ");
|
|
// Generate default member initializers for >= C++11.
|
|
std::string field_di;
|
|
if (opts_.g_cpp_std >= cpp::CPP_STD_11) {
|
|
field_di = "{}";
|
|
auto native_default = field.attributes.Lookup("native_default");
|
|
// Scalar types get parsed defaults, raw pointers get nullptrs.
|
|
if (IsScalar(field.value.type.base_type)) {
|
|
field_di =
|
|
" = " + (native_default ? std::string(native_default->constant)
|
|
: GetDefaultScalarValue(field, true));
|
|
} else if (field.value.type.base_type == BASE_TYPE_STRUCT) {
|
|
if (IsStruct(field.value.type) && native_default) {
|
|
field_di = " = " + native_default->constant;
|
|
}
|
|
}
|
|
}
|
|
code_.SetValue("FIELD_TYPE", full_type);
|
|
code_.SetValue("FIELD_NAME", Name(field));
|
|
code_.SetValue("FIELD_DI", field_di);
|
|
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}{{FIELD_DI}};";
|
|
}
|
|
}
|
|
|
|
// Returns true if `struct_def` needs a copy constructor and assignment
|
|
// operator because it has one or more table members, struct members with a
|
|
// custom cpp_type and non-naked pointer type, or vector members of those.
|
|
bool NeedsCopyCtorAssignOp(const StructDef& struct_def) {
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
const auto& type = field->value.type;
|
|
if (field->deprecated) continue;
|
|
if (type.base_type == BASE_TYPE_STRUCT) {
|
|
const auto cpp_type = field->attributes.Lookup("cpp_type");
|
|
const auto cpp_ptr_type = field->attributes.Lookup("cpp_ptr_type");
|
|
const bool is_ptr = !(IsStruct(type) && field->native_inline) ||
|
|
(cpp_type && cpp_ptr_type->constant != "naked");
|
|
if (is_ptr) {
|
|
return true;
|
|
}
|
|
} else if (IsVector(type)) {
|
|
const auto vec_type = type.VectorType();
|
|
if (vec_type.base_type == BASE_TYPE_UTYPE) continue;
|
|
const auto cpp_type = field->attributes.Lookup("cpp_type");
|
|
const auto cpp_ptr_type = field->attributes.Lookup("cpp_ptr_type");
|
|
const bool is_ptr = IsVectorOfPointers(*field) ||
|
|
(cpp_type && cpp_ptr_type->constant != "naked");
|
|
if (is_ptr) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Generate the default constructor for this struct. Properly initialize all
|
|
// scalar members with default values.
|
|
void GenDefaultConstructor(const StructDef& struct_def) {
|
|
code_.SetValue("NATIVE_NAME",
|
|
NativeName(Name(struct_def), &struct_def, opts_));
|
|
// In >= C++11, default member initializers are generated. To allow for
|
|
// aggregate initialization, do not emit a default constructor at all, with
|
|
// the exception of types that need a copy/move ctors and assignment
|
|
// operators.
|
|
if (opts_.g_cpp_std >= cpp::CPP_STD_11) {
|
|
if (NeedsCopyCtorAssignOp(struct_def)) {
|
|
code_ += " {{NATIVE_NAME}}() = default;";
|
|
}
|
|
return;
|
|
}
|
|
std::string initializer_list;
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
const auto& field = **it;
|
|
if (!field.deprecated && // Deprecated fields won't be accessible.
|
|
field.value.type.base_type != BASE_TYPE_UTYPE) {
|
|
auto cpp_type = field.attributes.Lookup("cpp_type");
|
|
auto native_default = field.attributes.Lookup("native_default");
|
|
// Scalar types get parsed defaults, raw pointers get nullptrs.
|
|
if (IsScalar(field.value.type.base_type)) {
|
|
if (!initializer_list.empty()) {
|
|
initializer_list += ",\n ";
|
|
}
|
|
initializer_list += Name(field);
|
|
initializer_list +=
|
|
"(" +
|
|
(native_default ? std::string(native_default->constant)
|
|
: GetDefaultScalarValue(field, true)) +
|
|
")";
|
|
} else if (field.value.type.base_type == BASE_TYPE_STRUCT) {
|
|
if (IsStruct(field.value.type)) {
|
|
if (native_default) {
|
|
if (!initializer_list.empty()) {
|
|
initializer_list += ",\n ";
|
|
}
|
|
initializer_list +=
|
|
Name(field) + "(" + native_default->constant + ")";
|
|
}
|
|
}
|
|
} else if (cpp_type && !IsVector(field.value.type)) {
|
|
if (!initializer_list.empty()) {
|
|
initializer_list += ",\n ";
|
|
}
|
|
initializer_list += Name(field) + "(0)";
|
|
}
|
|
}
|
|
}
|
|
if (!initializer_list.empty()) {
|
|
initializer_list = "\n : " + initializer_list;
|
|
}
|
|
|
|
code_.SetValue("INIT_LIST", initializer_list);
|
|
|
|
code_ += " {{NATIVE_NAME}}(){{INIT_LIST}} {";
|
|
code_ += " }";
|
|
}
|
|
|
|
// Generate the >= C++11 copy/move constructor and assignment operator
|
|
// declarations if required. Tables that are default-copyable do not get
|
|
// user-provided copy/move constructors and assignment operators so they
|
|
// remain aggregates.
|
|
void GenCopyMoveCtorAndAssigOpDecls(const StructDef& struct_def) {
|
|
if (opts_.g_cpp_std < cpp::CPP_STD_11) return;
|
|
if (!NeedsCopyCtorAssignOp(struct_def)) return;
|
|
code_.SetValue("NATIVE_NAME",
|
|
NativeName(Name(struct_def), &struct_def, opts_));
|
|
code_ += " {{NATIVE_NAME}}(const {{NATIVE_NAME}} &o);";
|
|
code_ +=
|
|
" {{NATIVE_NAME}}({{NATIVE_NAME}}&&) FLATBUFFERS_NOEXCEPT = "
|
|
"default;";
|
|
code_ +=
|
|
" {{NATIVE_NAME}} &operator=({{NATIVE_NAME}} o) FLATBUFFERS_NOEXCEPT;";
|
|
}
|
|
|
|
// Generate the >= C++11 copy constructor and assignment operator definitions.
|
|
void GenCopyCtorAssignOpDefs(const StructDef& struct_def) {
|
|
if (opts_.g_cpp_std < cpp::CPP_STD_11) return;
|
|
if (!NeedsCopyCtorAssignOp(struct_def)) return;
|
|
std::string initializer_list;
|
|
std::string vector_copies;
|
|
std::string swaps;
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
const auto& type = field->value.type;
|
|
if (field->deprecated || type.base_type == BASE_TYPE_UTYPE) continue;
|
|
if (type.base_type == BASE_TYPE_STRUCT) {
|
|
if (!initializer_list.empty()) {
|
|
initializer_list += ",\n ";
|
|
}
|
|
const auto cpp_type = field->attributes.Lookup("cpp_type");
|
|
const auto cpp_ptr_type = field->attributes.Lookup("cpp_ptr_type");
|
|
const std::string& type_name =
|
|
(cpp_type) ? cpp_type->constant
|
|
: GenTypeNative(type, /*invector*/ false, *field,
|
|
/*forcopy*/ true);
|
|
const bool is_ptr = !(IsStruct(type) && field->native_inline) ||
|
|
(cpp_type && cpp_ptr_type->constant != "naked");
|
|
CodeWriter cw;
|
|
cw.SetValue("FIELD", Name(*field));
|
|
cw.SetValue("TYPE", type_name);
|
|
if (is_ptr) {
|
|
cw +=
|
|
"{{FIELD}}((o.{{FIELD}}) ? new {{TYPE}}(*o.{{FIELD}}) : "
|
|
"nullptr)\\";
|
|
initializer_list += cw.ToString();
|
|
} else {
|
|
cw += "{{FIELD}}(o.{{FIELD}})\\";
|
|
initializer_list += cw.ToString();
|
|
}
|
|
} else if (IsVector(type)) {
|
|
const auto vec_type = type.VectorType();
|
|
if (vec_type.base_type == BASE_TYPE_UTYPE) continue;
|
|
const auto cpp_type = field->attributes.Lookup("cpp_type");
|
|
const auto cpp_ptr_type = field->attributes.Lookup("cpp_ptr_type");
|
|
const std::string& type_name =
|
|
(cpp_type) ? cpp_type->constant
|
|
: GenTypeNative(vec_type, /*invector*/ true, *field,
|
|
/*forcopy*/ true);
|
|
const bool is_ptr = IsVectorOfPointers(*field) ||
|
|
(cpp_type && cpp_ptr_type->constant != "naked");
|
|
CodeWriter cw(" ");
|
|
cw.SetValue("FIELD", Name(*field));
|
|
cw.SetValue("TYPE", type_name);
|
|
if (is_ptr) {
|
|
// Use emplace_back to construct the potentially-smart pointer element
|
|
// from a raw pointer to a new-allocated copy.
|
|
cw.IncrementIdentLevel();
|
|
cw += "{{FIELD}}.reserve(o.{{FIELD}}.size());";
|
|
cw +=
|
|
"for (const auto &{{FIELD}}_ : o.{{FIELD}}) { "
|
|
"{{FIELD}}.emplace_back(({{FIELD}}_) ? new {{TYPE}}(*{{FIELD}}_) "
|
|
": nullptr); }";
|
|
vector_copies += cw.ToString();
|
|
} else {
|
|
// For non-pointer elements, use std::vector's copy constructor in the
|
|
// initializer list. This will yield better performance than an insert
|
|
// range loop for trivially-copyable element types.
|
|
if (!initializer_list.empty()) {
|
|
initializer_list += ",\n ";
|
|
}
|
|
cw += "{{FIELD}}(o.{{FIELD}})\\";
|
|
initializer_list += cw.ToString();
|
|
}
|
|
} else {
|
|
if (!initializer_list.empty()) {
|
|
initializer_list += ",\n ";
|
|
}
|
|
CodeWriter cw;
|
|
cw.SetValue("FIELD", Name(*field));
|
|
cw += "{{FIELD}}(o.{{FIELD}})\\";
|
|
initializer_list += cw.ToString();
|
|
}
|
|
{
|
|
if (!swaps.empty()) {
|
|
swaps += "\n ";
|
|
}
|
|
CodeWriter cw;
|
|
cw.SetValue("FIELD", Name(*field));
|
|
cw += "std::swap({{FIELD}}, o.{{FIELD}});\\";
|
|
swaps += cw.ToString();
|
|
}
|
|
}
|
|
if (!initializer_list.empty()) {
|
|
initializer_list = "\n : " + initializer_list;
|
|
}
|
|
if (!swaps.empty()) {
|
|
swaps = " " + swaps;
|
|
}
|
|
|
|
code_.SetValue("NATIVE_NAME",
|
|
NativeName(Name(struct_def), &struct_def, opts_));
|
|
code_.SetValue("INIT_LIST", initializer_list);
|
|
code_.SetValue("VEC_COPY", vector_copies);
|
|
code_.SetValue("SWAPS", swaps);
|
|
|
|
code_ +=
|
|
"inline {{NATIVE_NAME}}::{{NATIVE_NAME}}(const {{NATIVE_NAME}} &o)"
|
|
"{{INIT_LIST}} {";
|
|
code_ += "{{VEC_COPY}}}\n";
|
|
code_ +=
|
|
"inline {{NATIVE_NAME}} &{{NATIVE_NAME}}::operator="
|
|
"({{NATIVE_NAME}} o) FLATBUFFERS_NOEXCEPT {";
|
|
code_ += "{{SWAPS}}";
|
|
code_ += " return *this;\n}\n";
|
|
}
|
|
|
|
void GenCompareOperator(const StructDef& struct_def,
|
|
const std::string& accessSuffix = "") {
|
|
// Do not generate compare operators for native types.
|
|
const auto native_type = struct_def.attributes.Lookup("native_type");
|
|
if (native_type) {
|
|
return;
|
|
}
|
|
|
|
std::string compare_op;
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
const auto& field = **it;
|
|
const auto accessor = Name(field) + accessSuffix;
|
|
const auto lhs_accessor = "lhs." + accessor;
|
|
const auto rhs_accessor = "rhs." + accessor;
|
|
if (!field.deprecated && // Deprecated fields won't be accessible.
|
|
field.value.type.base_type != BASE_TYPE_UTYPE &&
|
|
(!IsVector(field.value.type) ||
|
|
field.value.type.element != BASE_TYPE_UTYPE)) {
|
|
if (!compare_op.empty()) {
|
|
compare_op += " &&\n ";
|
|
}
|
|
if (struct_def.fixed || field.native_inline ||
|
|
field.value.type.base_type != BASE_TYPE_STRUCT) {
|
|
// If the field is a vector of tables, the table need to be compared
|
|
// by value, instead of by the default unique_ptr == operator which
|
|
// compares by address.
|
|
if (IsVectorOfPointers(field)) {
|
|
const auto type =
|
|
GenTypeNative(field.value.type.VectorType(), true, field);
|
|
const auto equal_length =
|
|
lhs_accessor + ".size() == " + rhs_accessor + ".size()";
|
|
const auto elements_equal =
|
|
"std::equal(" + lhs_accessor + ".cbegin(), " + lhs_accessor +
|
|
".cend(), " + rhs_accessor + ".cbegin(), [](" + type +
|
|
" const &a, " + type +
|
|
" const &b) { return (a == b) || (a && b && *a == *b); })";
|
|
|
|
compare_op += "(" + equal_length + " && " + elements_equal + ")";
|
|
} else if (field.value.type.base_type == BASE_TYPE_ARRAY) {
|
|
compare_op += "(*" + lhs_accessor + " == *" + rhs_accessor + ")";
|
|
} else {
|
|
compare_op += "(" + lhs_accessor + " == " + rhs_accessor + ")";
|
|
}
|
|
} else {
|
|
// Deep compare of std::unique_ptr. Null is not equal to empty.
|
|
std::string both_null =
|
|
"(" + lhs_accessor + " == " + rhs_accessor + ")";
|
|
std::string not_null_and_equal = "(lhs." + accessor + " && rhs." +
|
|
accessor + " && *lhs." + accessor +
|
|
" == *rhs." + accessor + ")";
|
|
compare_op += "(" + both_null + " || " + not_null_and_equal + ")";
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string cmp_lhs;
|
|
std::string cmp_rhs;
|
|
if (compare_op.empty()) {
|
|
cmp_lhs = "";
|
|
cmp_rhs = "";
|
|
compare_op = " return true;";
|
|
} else {
|
|
cmp_lhs = "lhs";
|
|
cmp_rhs = "rhs";
|
|
compare_op = " return\n " + compare_op + ";";
|
|
}
|
|
|
|
code_.SetValue("CMP_OP", compare_op);
|
|
code_.SetValue("CMP_LHS", cmp_lhs);
|
|
code_.SetValue("CMP_RHS", cmp_rhs);
|
|
code_ += "";
|
|
code_ +=
|
|
"inline bool operator==(const {{NATIVE_NAME}} &{{CMP_LHS}}, const "
|
|
"{{NATIVE_NAME}} &{{CMP_RHS}}) {";
|
|
code_ += "{{CMP_OP}}";
|
|
code_ += "}";
|
|
|
|
code_ += "";
|
|
code_ +=
|
|
"inline bool operator!=(const {{NATIVE_NAME}} &lhs, const "
|
|
"{{NATIVE_NAME}} &rhs) {";
|
|
code_ += " return !(lhs == rhs);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
void GenAbslHashValue(const StructDef& struct_def) {
|
|
code_ += "template <typename H>";
|
|
code_ += "inline H AbslHashValue(H h, const {{NATIVE_NAME}} &obj) {";
|
|
std::string combine_args;
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
continue;
|
|
}
|
|
if (IsArray(field->value.type)) {
|
|
const auto field_accessor = "obj." + Name(*field) + "()";
|
|
const auto& element_type = field->value.type.VectorType();
|
|
if (IsStruct(element_type)) {
|
|
code_ += " for (const auto* element : *" + field_accessor + ") {";
|
|
code_ += " h = H::combine(std::move(h), *element);";
|
|
code_ += " }";
|
|
} else {
|
|
code_ += " for (auto element : *" + field_accessor + ") {";
|
|
code_ += " h = H::combine(std::move(h), element);";
|
|
code_ += " }";
|
|
}
|
|
} else {
|
|
combine_args += ", obj." + Name(*field) + "()";
|
|
}
|
|
}
|
|
code_ += " return H::combine(std::move(h)" + combine_args + ");";
|
|
code_ += "}";
|
|
}
|
|
|
|
void GenOperatorNewDelete(const StructDef& struct_def) {
|
|
if (auto native_custom_alloc =
|
|
struct_def.attributes.Lookup("native_custom_alloc")) {
|
|
code_ += " inline void *operator new (std::size_t count) {";
|
|
code_ += " return " + native_custom_alloc->constant +
|
|
"<{{NATIVE_NAME}}>().allocate(count / sizeof({{NATIVE_NAME}}));";
|
|
code_ += " }";
|
|
code_ += " inline void operator delete (void *ptr) {";
|
|
code_ += " return " + native_custom_alloc->constant +
|
|
"<{{NATIVE_NAME}}>().deallocate(static_cast<{{NATIVE_NAME}}*>("
|
|
"ptr),1);";
|
|
code_ += " }";
|
|
}
|
|
}
|
|
|
|
void GenNativeTable(const StructDef& struct_def) {
|
|
const auto native_name = NativeName(Name(struct_def), &struct_def, opts_);
|
|
code_.SetValue("STRUCT_NAME", Name(struct_def));
|
|
code_.SetValue("NATIVE_NAME", native_name);
|
|
|
|
// Generate a C++ object that can hold an unpacked version of this table.
|
|
code_ += "struct {{NATIVE_NAME}} : public ::flatbuffers::NativeTable {";
|
|
code_ += " typedef {{STRUCT_NAME}} TableType;";
|
|
GenFullyQualifiedNameGetter(struct_def, native_name);
|
|
for (const auto field : struct_def.fields.vec) {
|
|
GenMember(*field);
|
|
}
|
|
GenOperatorNewDelete(struct_def);
|
|
GenDefaultConstructor(struct_def);
|
|
GenCopyMoveCtorAndAssigOpDecls(struct_def);
|
|
code_ += "};";
|
|
code_ += "";
|
|
}
|
|
|
|
// Adds a typedef to the binary schema type so one could get the bfbs based
|
|
// on the type at runtime.
|
|
void GenBinarySchemaTypeDef(const StructDef* struct_def) {
|
|
if (struct_def && opts_.binary_schema_gen_embed) {
|
|
code_ += " typedef " + WrapInNameSpace(*struct_def) +
|
|
"BinarySchema BinarySchema;";
|
|
}
|
|
}
|
|
|
|
void GenNativeTablePost(const StructDef& struct_def) {
|
|
if (opts_.gen_compare) {
|
|
const auto native_name = NativeName(Name(struct_def), &struct_def, opts_);
|
|
code_.SetValue("STRUCT_NAME", Name(struct_def));
|
|
code_.SetValue("NATIVE_NAME", native_name);
|
|
GenCompareOperator(struct_def);
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
// Generate the code to call the appropriate Verify function(s) for a field.
|
|
void GenVerifyCall(const FieldDef& field, const char* prefix) {
|
|
code_.SetValue("PRE", prefix);
|
|
code_.SetValue("NAME", Name(field));
|
|
code_.SetValue("REQUIRED", field.IsRequired() ? "Required" : "");
|
|
code_.SetValue("SIZE", GenTypeSize(field.value.type));
|
|
code_.SetValue("OFFSET", GenFieldOffsetName(field));
|
|
if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type)) {
|
|
code_.SetValue("ALIGN", NumToString(InlineAlignment(field.value.type)));
|
|
code_ +=
|
|
"{{PRE}}VerifyField{{REQUIRED}}<{{SIZE}}>(verifier, "
|
|
"{{OFFSET}}, {{ALIGN}})\\";
|
|
} else {
|
|
code_.SetValue("OFFSET_SIZE", field.offset64 ? "64" : "");
|
|
code_ +=
|
|
"{{PRE}}VerifyOffset{{OFFSET_SIZE}}{{REQUIRED}}(verifier, "
|
|
"{{OFFSET}})\\";
|
|
}
|
|
|
|
switch (field.value.type.base_type) {
|
|
case BASE_TYPE_UNION: {
|
|
code_.SetValue("ENUM_NAME", field.value.type.enum_def->name);
|
|
code_.SetValue("SUFFIX", UnionTypeFieldSuffix());
|
|
code_ +=
|
|
"{{PRE}}Verify{{ENUM_NAME}}(verifier, {{NAME}}(), "
|
|
"{{NAME}}{{SUFFIX}}())\\";
|
|
break;
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
if (!field.value.type.struct_def->fixed) {
|
|
code_ += "{{PRE}}verifier.VerifyTable({{NAME}}())\\";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_STRING: {
|
|
if (field.value.constant != "0") {
|
|
if (field.offset64) {
|
|
code_ +=
|
|
"{{PRE}}VerifyStringWithDefault<::flatbuffers::uoffset64_t>("
|
|
"verifier, "
|
|
"{{OFFSET}})\\";
|
|
} else {
|
|
code_ += "{{PRE}}VerifyStringWithDefault(verifier, {{OFFSET}})\\";
|
|
}
|
|
} else {
|
|
code_ += "{{PRE}}verifier.VerifyString({{NAME}}())\\";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_VECTOR64:
|
|
case BASE_TYPE_VECTOR: {
|
|
if (field.value.constant == "[]") {
|
|
const auto& vec_type = field.value.type.VectorType();
|
|
const std::string vtype_wire = GenTypeWire(
|
|
vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
|
|
std::string verify_call;
|
|
if (field.offset64) {
|
|
verify_call = "{{PRE}}VerifyVector64WithDefault<" + vtype_wire;
|
|
} else {
|
|
verify_call = "{{PRE}}VerifyVectorWithDefault<" + vtype_wire;
|
|
}
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
|
verify_call += ", ::flatbuffers::uoffset64_t";
|
|
}
|
|
verify_call += ">(verifier, {{OFFSET}})\\";
|
|
code_ += verify_call;
|
|
} else {
|
|
code_ += "{{PRE}}verifier.VerifyVector({{NAME}}())\\";
|
|
}
|
|
|
|
switch (field.value.type.element) {
|
|
case BASE_TYPE_STRING: {
|
|
code_ += "{{PRE}}verifier.VerifyVectorOfStrings({{NAME}}())\\";
|
|
break;
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
if (!field.value.type.struct_def->fixed) {
|
|
code_ += "{{PRE}}verifier.VerifyVectorOfTables({{NAME}}())\\";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_UNION: {
|
|
code_.SetValue("ENUM_NAME", field.value.type.enum_def->name);
|
|
code_ +=
|
|
"{{PRE}}Verify{{ENUM_NAME}}Vector(verifier, {{NAME}}(), "
|
|
"{{NAME}}_type())\\";
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
auto nfn = GetNestedFlatBufferName(field);
|
|
if (!nfn.empty()) {
|
|
code_.SetValue("CPP_NAME", nfn);
|
|
// FIXME: file_identifier.
|
|
code_ +=
|
|
"{{PRE}}verifier.template VerifyNestedFlatBuffer<{{CPP_NAME}}>"
|
|
"({{NAME}}(), nullptr)\\";
|
|
} else if (field.flexbuffer) {
|
|
code_ +=
|
|
"{{PRE}}flexbuffers::VerifyNestedFlexBuffer"
|
|
"({{NAME}}(), verifier)\\";
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GenComparatorForStruct(const StructDef& struct_def, size_t space_size,
|
|
const std::string lhs_struct_literal,
|
|
const std::string rhs_struct_literal) {
|
|
code_.SetValue("LHS_PREFIX", lhs_struct_literal);
|
|
code_.SetValue("RHS_PREFIX", rhs_struct_literal);
|
|
std::string space(space_size, ' ');
|
|
for (const auto& curr_field : struct_def.fields.vec) {
|
|
const auto curr_field_name = Name(*curr_field);
|
|
code_.SetValue("CURR_FIELD_NAME", curr_field_name);
|
|
code_.SetValue("LHS", lhs_struct_literal + "_" + curr_field_name);
|
|
code_.SetValue("RHS", rhs_struct_literal + "_" + curr_field_name);
|
|
const bool is_scalar = IsScalar(curr_field->value.type.base_type);
|
|
const bool is_array = IsArray(curr_field->value.type);
|
|
const bool is_struct = IsStruct(curr_field->value.type);
|
|
|
|
// If encouter a key field, call KeyCompareWithValue to compare this
|
|
// field.
|
|
if (curr_field->key) {
|
|
code_ += space +
|
|
"const auto {{RHS}} = {{RHS_PREFIX}}.{{CURR_FIELD_NAME}}();";
|
|
code_ += space +
|
|
"const auto {{CURR_FIELD_NAME}}_compare_result = "
|
|
"{{LHS_PREFIX}}.KeyCompareWithValue({{RHS}});";
|
|
|
|
code_ += space + "if ({{CURR_FIELD_NAME}}_compare_result != 0)";
|
|
code_ += space + " return {{CURR_FIELD_NAME}}_compare_result;";
|
|
continue;
|
|
}
|
|
|
|
code_ +=
|
|
space + "const auto {{LHS}} = {{LHS_PREFIX}}.{{CURR_FIELD_NAME}}();";
|
|
code_ +=
|
|
space + "const auto {{RHS}} = {{RHS_PREFIX}}.{{CURR_FIELD_NAME}}();";
|
|
if (is_scalar) {
|
|
code_ += space + "if ({{LHS}} != {{RHS}})";
|
|
code_ += space +
|
|
" return static_cast<int>({{LHS}} > {{RHS}}) - "
|
|
"static_cast<int>({{LHS}} < {{RHS}});";
|
|
} else if (is_array) {
|
|
const auto& elem_type = curr_field->value.type.VectorType();
|
|
code_ +=
|
|
space +
|
|
"for (::flatbuffers::uoffset_t i = 0; i < {{LHS}}->size(); i++) {";
|
|
code_ += space + " const auto {{LHS}}_elem = {{LHS}}->Get(i);";
|
|
code_ += space + " const auto {{RHS}}_elem = {{RHS}}->Get(i);";
|
|
if (IsScalar(elem_type.base_type)) {
|
|
code_ += space + " if ({{LHS}}_elem != {{RHS}}_elem)";
|
|
code_ += space +
|
|
" return static_cast<int>({{LHS}}_elem > {{RHS}}_elem) - "
|
|
"static_cast<int>({{LHS}}_elem < {{RHS}}_elem);";
|
|
code_ += space + "}";
|
|
|
|
} else if (IsStruct(elem_type)) {
|
|
if (curr_field->key) {
|
|
code_ += space +
|
|
"const auto {{CURR_FIELD_NAME}}_compare_result = "
|
|
"{{LHS_PREFIX}}.KeyCompareWithValue({{RHS}});";
|
|
code_ += space + "if ({{CURR_FIELD_NAME}}_compare_result != 0)";
|
|
code_ += space + " return {{CURR_FIELD_NAME}}_compare_result;";
|
|
continue;
|
|
}
|
|
GenComparatorForStruct(
|
|
*curr_field->value.type.struct_def, space_size + 2,
|
|
code_.GetValue("LHS") + "_elem", code_.GetValue("RHS") + "_elem");
|
|
|
|
code_ += space + "}";
|
|
}
|
|
|
|
} else if (is_struct) {
|
|
GenComparatorForStruct(*curr_field->value.type.struct_def, space_size,
|
|
code_.GetValue("LHS"), code_.GetValue("RHS"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate CompareWithValue method for a key field.
|
|
void GenKeyFieldMethods(const FieldDef& field) {
|
|
FLATBUFFERS_ASSERT(field.key);
|
|
const bool is_string = IsString(field.value.type);
|
|
const bool is_array = IsArray(field.value.type);
|
|
const bool is_struct = IsStruct(field.value.type);
|
|
// Generate KeyCompareLessThan function
|
|
code_ +=
|
|
" bool KeyCompareLessThan(const {{STRUCT_NAME}} * const o) const {";
|
|
if (is_string) {
|
|
// use operator< of ::flatbuffers::String
|
|
code_ += " return *{{FIELD_NAME}}() < *o->{{FIELD_NAME}}();";
|
|
} else if (is_array || is_struct) {
|
|
code_ += " return KeyCompareWithValue(o->{{FIELD_NAME}}()) < 0;";
|
|
} else {
|
|
code_ += " return {{FIELD_NAME}}() < o->{{FIELD_NAME}}();";
|
|
}
|
|
code_ += " }";
|
|
|
|
// Generate KeyCompareWithValue function
|
|
if (is_string) {
|
|
// Compares key against a null-terminated char array.
|
|
code_ += " int KeyCompareWithValue(const char *_{{FIELD_NAME}}) const {";
|
|
code_ += " return strcmp({{FIELD_NAME}}()->c_str(), _{{FIELD_NAME}});";
|
|
code_ += " }";
|
|
// Compares key against any string-like object (e.g. std::string_view or
|
|
// std::string) that implements operator< comparison with const char*.
|
|
code_ += " template<typename StringType>";
|
|
code_ +=
|
|
" int KeyCompareWithValue(const StringType& _{{FIELD_NAME}}) const "
|
|
"{";
|
|
code_ +=
|
|
" if ({{FIELD_NAME}}()->c_str() < _{{FIELD_NAME}}) return -1;";
|
|
code_ += " if (_{{FIELD_NAME}} < {{FIELD_NAME}}()->c_str()) return 1;";
|
|
code_ += " return 0;";
|
|
} else if (is_array) {
|
|
const auto& elem_type = field.value.type.VectorType();
|
|
std::string input_type = "::flatbuffers::Array<" +
|
|
GenTypeGet(elem_type, "", "", "", false) + ", " +
|
|
NumToString(elem_type.fixed_length) + ">";
|
|
code_.SetValue("INPUT_TYPE", input_type);
|
|
code_ +=
|
|
" int KeyCompareWithValue(const {{INPUT_TYPE}} *_{{FIELD_NAME}}"
|
|
") const {";
|
|
code_ +=
|
|
" const {{INPUT_TYPE}} *curr_{{FIELD_NAME}} = {{FIELD_NAME}}();";
|
|
code_ +=
|
|
" for (::flatbuffers::uoffset_t i = 0; i < "
|
|
"curr_{{FIELD_NAME}}->size(); i++) {";
|
|
|
|
if (IsScalar(elem_type.base_type)) {
|
|
code_ += " const auto lhs = curr_{{FIELD_NAME}}->Get(i);";
|
|
code_ += " const auto rhs = _{{FIELD_NAME}}->Get(i);";
|
|
code_ += " if (lhs != rhs)";
|
|
code_ +=
|
|
" return static_cast<int>(lhs > rhs)"
|
|
" - static_cast<int>(lhs < rhs);";
|
|
} else if (IsStruct(elem_type)) {
|
|
code_ +=
|
|
" const auto &lhs_{{FIELD_NAME}} = "
|
|
"*(curr_{{FIELD_NAME}}->Get(i));";
|
|
code_ +=
|
|
" const auto &rhs_{{FIELD_NAME}} = "
|
|
"*(_{{FIELD_NAME}}->Get(i));";
|
|
GenComparatorForStruct(*elem_type.struct_def, 6,
|
|
"lhs_" + code_.GetValue("FIELD_NAME"),
|
|
"rhs_" + code_.GetValue("FIELD_NAME"));
|
|
}
|
|
code_ += " }";
|
|
code_ += " return 0;";
|
|
} else if (is_struct) {
|
|
const auto* struct_def = field.value.type.struct_def;
|
|
code_.SetValue("INPUT_TYPE",
|
|
GenTypeGet(field.value.type, "", "", "", false));
|
|
code_ +=
|
|
" int KeyCompareWithValue(const {{INPUT_TYPE}} &_{{FIELD_NAME}}) "
|
|
"const {";
|
|
code_ += " const auto &lhs_{{FIELD_NAME}} = {{FIELD_NAME}}();";
|
|
code_ += " const auto &rhs_{{FIELD_NAME}} = _{{FIELD_NAME}};";
|
|
GenComparatorForStruct(*struct_def, 4,
|
|
"lhs_" + code_.GetValue("FIELD_NAME"),
|
|
"rhs_" + code_.GetValue("FIELD_NAME"));
|
|
code_ += " return 0;";
|
|
|
|
} else {
|
|
FLATBUFFERS_ASSERT(IsScalar(field.value.type.base_type));
|
|
auto type = GenTypeBasic(field.value.type, false);
|
|
if (opts_.scoped_enums && field.value.type.enum_def &&
|
|
IsScalar(field.value.type.base_type)) {
|
|
type = GenTypeGet(field.value.type, " ", "const ", " *", true);
|
|
}
|
|
// Returns {field<val: -1, field==val: 0, field>val: +1}.
|
|
code_.SetValue("KEY_TYPE", type);
|
|
code_ +=
|
|
" int KeyCompareWithValue({{KEY_TYPE}} _{{FIELD_NAME}}) const {";
|
|
code_ +=
|
|
" return static_cast<int>({{FIELD_NAME}}() > _{{FIELD_NAME}}) - "
|
|
"static_cast<int>({{FIELD_NAME}}() < _{{FIELD_NAME}});";
|
|
}
|
|
code_ += " }";
|
|
}
|
|
|
|
void GenTableUnionAsGetters(const FieldDef& field, bool is_mutable) {
|
|
const auto& type = field.value.type;
|
|
auto u = type.enum_def;
|
|
|
|
code_.SetValue("MUTABLE_EXT", is_mutable ? "" : " const");
|
|
code_.SetValue("MUTABLE", is_mutable ? "mutable_" : "");
|
|
|
|
if (!type.enum_def->uses_multiple_type_instances)
|
|
code_ +=
|
|
" template<typename T>"
|
|
"{{MUTABLE_EXT}} T *{{MUTABLE}}{{NULLABLE_EXT}}{{FIELD_NAME}}_as()"
|
|
"{{MUTABLE_EXT}};";
|
|
|
|
for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
|
|
auto& ev = **u_it;
|
|
if (ev.union_type.base_type == BASE_TYPE_NONE) {
|
|
continue;
|
|
}
|
|
auto full_struct_name = GetUnionElement(ev, false, opts_);
|
|
|
|
// @TODO: Mby make this decisions more universal? How?
|
|
code_.SetValue("U_GET_TYPE",
|
|
EscapeKeyword(Name(field) + UnionTypeFieldSuffix()));
|
|
code_.SetValue("U_ELEMENT_TYPE", WrapInNameSpace(u->defined_namespace,
|
|
GetEnumValUse(*u, ev)));
|
|
code_.SetValue("U_FIELD_TYPE",
|
|
(is_mutable ? "" : "const ") + full_struct_name + " *");
|
|
code_.SetValue("U_FIELD_NAME", Name(field) + "_as_" + Name(ev));
|
|
code_.SetValue("U_NULLABLE", NullableExtension());
|
|
|
|
// `const Type *union_name_asType() const` accessor.
|
|
// and `Type *mutable_union_name_asType()` accessor.
|
|
code_ +=
|
|
" {{U_FIELD_TYPE}}{{U_NULLABLE}}{{MUTABLE}}{{U_FIELD_NAME}}()"
|
|
"{{MUTABLE_EXT}} {";
|
|
code_ +=
|
|
" return {{U_GET_TYPE}}() == {{U_ELEMENT_TYPE}} ? "
|
|
"static_cast<{{U_FIELD_TYPE}}>({{MUTABLE}}{{FIELD_NAME}}()) "
|
|
": nullptr;";
|
|
code_ += " }";
|
|
}
|
|
}
|
|
|
|
void GenTableFieldGetter(const FieldDef& field) {
|
|
const auto& type = field.value.type;
|
|
const auto offset_str = GenFieldOffsetName(field);
|
|
|
|
GenComment(field.doc_comment, " ");
|
|
// Call a different accessor for pointers, that indirects.
|
|
if (!field.IsScalarOptional()) {
|
|
const bool is_scalar = IsScalar(type.base_type);
|
|
std::string accessor;
|
|
std::string offset_size = "";
|
|
if (is_scalar) {
|
|
accessor = "GetField<";
|
|
} else if (IsStruct(type)) {
|
|
accessor = "GetStruct<";
|
|
} else {
|
|
if (field.offset64) {
|
|
accessor = "GetPointer64<";
|
|
} else {
|
|
accessor = "GetPointer<";
|
|
}
|
|
}
|
|
auto offset_type = GenTypeGet(type, "", "const ", " *", false);
|
|
auto call = accessor + offset_type + ">(" + offset_str;
|
|
// Default value as second arg for non-pointer types.
|
|
if (is_scalar) {
|
|
call += ", " + GenDefaultConstant(field);
|
|
}
|
|
call += ")";
|
|
|
|
std::string afterptr = " *" + NullableExtension();
|
|
code_.SetValue("FIELD_TYPE",
|
|
GenTypeGet(type, " ", "const ", afterptr.c_str(), true));
|
|
code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, call));
|
|
code_.SetValue("NULLABLE_EXT", NullableExtension());
|
|
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
|
|
if (IsVector(type) && field.value.constant == "[]") {
|
|
const auto& vec_type = type.VectorType();
|
|
const std::string vtype_wire = GenTypeWire(
|
|
vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
|
|
std::string get_call;
|
|
if (field.offset64) {
|
|
get_call = " return GetVectorPointer64OrEmpty<" + vtype_wire;
|
|
} else {
|
|
get_call = " return GetVectorPointerOrEmpty<" + vtype_wire;
|
|
}
|
|
if (type.base_type == BASE_TYPE_VECTOR64) {
|
|
get_call += ", ::flatbuffers::uoffset64_t";
|
|
}
|
|
get_call += ">(" + offset_str + ");";
|
|
code_ += get_call;
|
|
} else if (IsString(type) && field.value.constant != "0") {
|
|
std::string escaped;
|
|
flatbuffers::EscapeString(field.value.constant.c_str(),
|
|
field.value.constant.length(), &escaped,
|
|
true, false);
|
|
code_ += " auto* ptr = {{FIELD_VALUE}};";
|
|
code_ += " if (ptr) return ptr;";
|
|
code_ += " static const struct { uint32_t len; const char s[" +
|
|
NumToString(field.value.constant.length() + 1) +
|
|
"]; } bfbs_string = { " +
|
|
NumToString(field.value.constant.length()) + ", " +
|
|
escaped + " };";
|
|
code_ +=
|
|
" return reinterpret_cast<const ::flatbuffers::String "
|
|
" *>(&bfbs_string);";
|
|
} else {
|
|
code_ += " return {{FIELD_VALUE}};";
|
|
}
|
|
code_ += " }";
|
|
} else {
|
|
auto wire_type = GenTypeBasic(type, false);
|
|
auto face_type = GenTypeBasic(type, true);
|
|
auto opt_value = "GetOptional<" + wire_type + ", " + face_type + ">(" +
|
|
offset_str + ")";
|
|
code_.SetValue("FIELD_TYPE", GenOptionalDecl(type));
|
|
code_ += " {{FIELD_TYPE}} {{FIELD_NAME}}() const {";
|
|
code_ += " return " + opt_value + ";";
|
|
code_ += " }";
|
|
}
|
|
|
|
if (type.base_type == BASE_TYPE_UNION) {
|
|
GenTableUnionAsGetters(field, false);
|
|
}
|
|
}
|
|
|
|
void GenTableFieldType(const FieldDef& field) {
|
|
const auto& type = field.value.type;
|
|
const auto offset_str = GenFieldOffsetName(field);
|
|
if (!field.IsScalarOptional()) {
|
|
std::string afterptr = " *" + NullableExtension();
|
|
code_.SetValue("FIELD_TYPE",
|
|
GenTypeGet(type, "", "const ", afterptr.c_str(), true));
|
|
code_ += " {{FIELD_TYPE}}\\";
|
|
} else {
|
|
code_.SetValue("FIELD_TYPE", GenOptionalDecl(type));
|
|
code_ += " {{FIELD_TYPE}}\\";
|
|
}
|
|
}
|
|
|
|
void GenStructFieldType(const FieldDef& field) {
|
|
const auto is_array = IsArray(field.value.type);
|
|
std::string field_type =
|
|
GenTypeGet(field.value.type, "", is_array ? "" : "const ",
|
|
is_array ? "" : " &", true);
|
|
code_.SetValue("FIELD_TYPE", field_type);
|
|
code_ += " {{FIELD_TYPE}}\\";
|
|
}
|
|
|
|
void GenFieldTypeHelper(const StructDef& struct_def) {
|
|
if (struct_def.fields.vec.empty()) {
|
|
return;
|
|
}
|
|
code_ += " template<size_t Index>";
|
|
code_ += " using FieldType = \\";
|
|
code_ += "decltype(std::declval<type>().get_field<Index>());";
|
|
}
|
|
|
|
void GenIndexBasedFieldGetter(const StructDef& struct_def) {
|
|
if (struct_def.fields.vec.empty()) {
|
|
return;
|
|
}
|
|
code_ += " template<size_t Index>";
|
|
code_ += " auto get_field() const {";
|
|
|
|
size_t index = 0;
|
|
bool need_else = false;
|
|
// Generate one index-based getter for each field.
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
// Deprecated fields won't be accessible.
|
|
continue;
|
|
}
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
code_.SetValue("FIELD_INDEX",
|
|
std::to_string(static_cast<long long>(index++)));
|
|
if (need_else) {
|
|
code_ += " else \\";
|
|
} else {
|
|
code_ += " \\";
|
|
}
|
|
need_else = true;
|
|
code_ += "if constexpr (Index == {{FIELD_INDEX}}) \\";
|
|
code_ += "return {{FIELD_NAME}}();";
|
|
}
|
|
code_ += " else static_assert(Index != -1, \"Invalid Field Index\");";
|
|
code_ += " }";
|
|
}
|
|
|
|
// Sample for Vec3:
|
|
//
|
|
// static constexpr std::array<const char *, 3> field_names = {
|
|
// "x",
|
|
// "y",
|
|
// "z"
|
|
// };
|
|
//
|
|
void GenFieldNames(const StructDef& struct_def) {
|
|
code_ += " static constexpr std::array<\\";
|
|
code_ += "const char *, fields_number> field_names = {\\";
|
|
if (struct_def.fields.vec.empty()) {
|
|
code_ += "};";
|
|
return;
|
|
}
|
|
code_ += "";
|
|
// Generate the field_names elements.
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
const auto& field = **it;
|
|
if (field.deprecated) {
|
|
// Deprecated fields won't be accessible.
|
|
continue;
|
|
}
|
|
code_.SetValue("FIELD_NAME", Name(field));
|
|
code_ += R"( "{{FIELD_NAME}}"\)";
|
|
if (it + 1 != struct_def.fields.vec.end()) {
|
|
code_ += ",";
|
|
}
|
|
}
|
|
code_ += "\n };";
|
|
}
|
|
|
|
void GenFieldsNumber(const StructDef& struct_def) {
|
|
const auto non_deprecated_field_count = std::count_if(
|
|
struct_def.fields.vec.begin(), struct_def.fields.vec.end(),
|
|
[](const FieldDef* field) { return !field->deprecated; });
|
|
code_.SetValue(
|
|
"FIELD_COUNT",
|
|
std::to_string(static_cast<long long>(non_deprecated_field_count)));
|
|
code_ += " static constexpr size_t fields_number = {{FIELD_COUNT}};";
|
|
}
|
|
|
|
void GenTraitsStruct(const StructDef& struct_def) {
|
|
code_.SetValue(
|
|
"FULLY_QUALIFIED_NAME",
|
|
struct_def.defined_namespace->GetFullyQualifiedName(Name(struct_def)));
|
|
code_ += "struct {{STRUCT_NAME}}::Traits {";
|
|
code_ += " using type = {{STRUCT_NAME}};";
|
|
if (!struct_def.fixed) {
|
|
// We have a table and not a struct.
|
|
code_ += " static auto constexpr Create = Create{{STRUCT_NAME}};";
|
|
}
|
|
if (opts_.cpp_static_reflection) {
|
|
code_ += " static constexpr auto name = \"{{STRUCT_NAME}}\";";
|
|
code_ +=
|
|
" static constexpr auto fully_qualified_name = "
|
|
"\"{{FULLY_QUALIFIED_NAME}}\";";
|
|
GenFieldsNumber(struct_def);
|
|
GenFieldNames(struct_def);
|
|
GenFieldTypeHelper(struct_def);
|
|
}
|
|
code_ += "};";
|
|
code_ += "";
|
|
}
|
|
|
|
void GenTableFieldSetter(const FieldDef& field) {
|
|
const auto& type = field.value.type;
|
|
const bool is_scalar = IsScalar(type.base_type);
|
|
if (is_scalar && IsUnion(type))
|
|
return; // changing of a union's type is forbidden
|
|
|
|
auto offset_str = GenFieldOffsetName(field);
|
|
if (is_scalar) {
|
|
const auto wire_type = GenTypeWire(type, "", false, field.offset64);
|
|
code_.SetValue("SET_FN", "SetField<" + wire_type + ">");
|
|
code_.SetValue("OFFSET_NAME", offset_str);
|
|
code_.SetValue("FIELD_TYPE", GenTypeBasic(type, true));
|
|
code_.SetValue("FIELD_VALUE",
|
|
GenUnderlyingCast(field, false, "_" + Name(field)));
|
|
|
|
code_ += " bool mutate_{{FIELD_NAME}}({{FIELD_TYPE}} _{{FIELD_NAME}}\\";
|
|
if (!field.IsScalarOptional()) {
|
|
code_.SetValue("DEFAULT_VALUE", GenDefaultConstant(field));
|
|
code_.SetValue(
|
|
"INTERFACE_DEFAULT_VALUE",
|
|
GenUnderlyingCast(field, true, GenDefaultConstant(field)));
|
|
|
|
// GenUnderlyingCast for a bool field generates 0 != 0
|
|
// So the type has to be checked and the appropriate default chosen
|
|
if (IsBool(field.value.type.base_type)) {
|
|
code_ += " = {{DEFAULT_VALUE}}) {";
|
|
} else {
|
|
code_ += " = {{INTERFACE_DEFAULT_VALUE}}) {";
|
|
}
|
|
code_ +=
|
|
" return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}}, "
|
|
"{{DEFAULT_VALUE}});";
|
|
} else {
|
|
code_ += ") {";
|
|
code_ += " return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}});";
|
|
}
|
|
code_ += " }";
|
|
} else {
|
|
auto postptr = " *" + NullableExtension();
|
|
auto wire_type = GenTypeGet(type, " ", "", postptr.c_str(), true);
|
|
code_.SetValue("FIELD_TYPE", wire_type);
|
|
|
|
// mutable union accessors
|
|
if (type.base_type == BASE_TYPE_UNION) {
|
|
GenTableUnionAsGetters(field, true);
|
|
}
|
|
|
|
if (IsVector(type) && field.value.constant == "[]") {
|
|
const auto& vec_type = type.VectorType();
|
|
const std::string vtype_wire = GenTypeWire(
|
|
vec_type, "", VectorElementUserFacing(vec_type), field.offset64);
|
|
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
|
|
std::string get_call;
|
|
if (field.offset64) {
|
|
get_call =
|
|
" return GetMutableVectorPointer64OrEmpty<" + vtype_wire;
|
|
} else {
|
|
get_call = " return GetMutableVectorPointerOrEmpty<" + vtype_wire;
|
|
}
|
|
if (type.base_type == BASE_TYPE_VECTOR64) {
|
|
get_call += ", ::flatbuffers::uoffset64_t";
|
|
}
|
|
get_call += ">(" + offset_str + ");";
|
|
code_ += get_call;
|
|
code_ += " }";
|
|
} else {
|
|
const std::string accessor = [&]() {
|
|
if (IsStruct(type)) {
|
|
return "GetStruct<";
|
|
}
|
|
if (field.offset64) {
|
|
return "GetPointer64<";
|
|
}
|
|
return "GetPointer<";
|
|
}();
|
|
auto underlying = accessor + wire_type + ">(" + offset_str + ")";
|
|
code_.SetValue("FIELD_VALUE",
|
|
GenUnderlyingCast(field, true, underlying));
|
|
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
|
|
code_ += " return {{FIELD_VALUE}};";
|
|
code_ += " }";
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string GetNestedFlatBufferName(const FieldDef& field) {
|
|
auto nested = field.attributes.Lookup("nested_flatbuffer");
|
|
if (!nested) return "";
|
|
std::string qualified_name = nested->constant;
|
|
auto nested_root = parser_.LookupStruct(nested->constant);
|
|
if (nested_root == nullptr) {
|
|
qualified_name =
|
|
parser_.current_namespace_->GetFullyQualifiedName(nested->constant);
|
|
nested_root = parser_.LookupStruct(qualified_name);
|
|
}
|
|
FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser.
|
|
(void)nested_root;
|
|
return TranslateNameSpace(qualified_name);
|
|
}
|
|
|
|
// Generate an accessor struct, builder structs & function for a table.
|
|
void GenTable(const StructDef& struct_def) {
|
|
// Don't generate an object API struct for the table if it is a native type.
|
|
const auto native_type = struct_def.attributes.Lookup("native_type");
|
|
if (opts_.generate_object_based_api && !native_type) {
|
|
GenNativeTable(struct_def);
|
|
}
|
|
|
|
// Generate an accessor struct, with methods of the form:
|
|
// type name() const { return GetField<type>(offset, defaultval); }
|
|
GenComment(struct_def.doc_comment);
|
|
|
|
const auto native_name = NativeName(Name(struct_def), &struct_def, opts_);
|
|
code_.SetValue("NATIVE_NAME", native_name);
|
|
code_.SetValue("STRUCT_NAME", Name(struct_def));
|
|
code_ +=
|
|
"struct {{STRUCT_NAME}} FLATBUFFERS_FINAL_CLASS"
|
|
" : private ::flatbuffers::Table {";
|
|
if (opts_.generate_object_based_api) {
|
|
code_ += " typedef {{NATIVE_NAME}} NativeTableType;";
|
|
}
|
|
code_ += " typedef {{STRUCT_NAME}}Builder Builder;";
|
|
GenBinarySchemaTypeDef(parser_.root_struct_def_);
|
|
|
|
if (opts_.g_cpp_std >= cpp::CPP_STD_17) {
|
|
code_ += " struct Traits;";
|
|
}
|
|
if (opts_.mini_reflect != IDLOptions::kNone) {
|
|
code_ +=
|
|
" static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {";
|
|
code_ += " return {{STRUCT_NAME}}TypeTable();";
|
|
code_ += " }";
|
|
}
|
|
|
|
GenFullyQualifiedNameGetter(struct_def, Name(struct_def));
|
|
|
|
// Generate field id constants.
|
|
if (!struct_def.fields.vec.empty()) {
|
|
// We need to add a trailing comma to all elements except the last one as
|
|
// older versions of gcc complain about this.
|
|
code_.SetValue("SEP", "");
|
|
code_ +=
|
|
" enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {";
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
// Deprecated fields won't be accessible.
|
|
continue;
|
|
}
|
|
|
|
code_.SetValue("OFFSET_NAME", GenFieldOffsetName(*field));
|
|
code_.SetValue("OFFSET_VALUE", NumToString(field->value.offset));
|
|
code_ += "{{SEP}} {{OFFSET_NAME}} = {{OFFSET_VALUE}}\\";
|
|
code_.SetValue("SEP", ",\n");
|
|
}
|
|
code_ += "";
|
|
code_ += " };";
|
|
}
|
|
|
|
// Generate the accessors.
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
// Deprecated fields won't be accessible.
|
|
continue;
|
|
}
|
|
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
GenTableFieldGetter(*field);
|
|
if (opts_.mutable_buffer) {
|
|
GenTableFieldSetter(*field);
|
|
}
|
|
|
|
auto nfn = GetNestedFlatBufferName(*field);
|
|
if (!nfn.empty()) {
|
|
code_.SetValue("CPP_NAME", nfn);
|
|
code_ += " const {{CPP_NAME}} *{{FIELD_NAME}}_nested_root() const {";
|
|
code_ += " const auto _f = {{FIELD_NAME}}();";
|
|
code_ +=
|
|
" return _f ? ::flatbuffers::GetRoot<{{CPP_NAME}}>(_f->Data())";
|
|
code_ += " : nullptr;";
|
|
code_ += " }";
|
|
}
|
|
|
|
if (field->flexbuffer) {
|
|
code_ +=
|
|
" flexbuffers::Reference {{FIELD_NAME}}_flexbuffer_root()"
|
|
" const {";
|
|
// Both Data() and size() are const-methods, therefore call order
|
|
// doesn't matter.
|
|
code_ += " const auto _f = {{FIELD_NAME}}();";
|
|
code_ += " return _f ? flexbuffers::GetRoot(_f->Data(), _f->size())";
|
|
code_ += " : flexbuffers::Reference();";
|
|
code_ += " }";
|
|
}
|
|
|
|
// Generate a comparison function for this field if it is a key.
|
|
if (field->key) {
|
|
GenKeyFieldMethods(*field);
|
|
}
|
|
}
|
|
|
|
if (opts_.cpp_static_reflection) {
|
|
GenIndexBasedFieldGetter(struct_def);
|
|
}
|
|
|
|
// Generate a verifier function that can check a buffer from an untrusted
|
|
// source will never cause reads outside the buffer.
|
|
code_ += " template <bool B = false>";
|
|
code_ +=
|
|
" bool Verify(::flatbuffers::VerifierTemplate<B> "
|
|
"&verifier) const {";
|
|
code_ += " return VerifyTableStart(verifier)\\";
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
continue;
|
|
}
|
|
GenVerifyCall(*field, " &&\n ");
|
|
}
|
|
|
|
code_ += " &&\n verifier.EndTable();";
|
|
code_ += " }";
|
|
|
|
if (opts_.generate_object_based_api) {
|
|
// Generate the UnPack() pre declaration.
|
|
code_ += " " + TableUnPackSignature(struct_def, true, opts_) + ";";
|
|
code_ += " " + TableUnPackToSignature(struct_def, true, opts_) + ";";
|
|
code_ += " " + TablePackSignature(struct_def, true, opts_) + ";";
|
|
}
|
|
|
|
code_ += "};"; // End of table.
|
|
code_ += "";
|
|
|
|
// Explicit specializations for union accessors
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated || field->value.type.base_type != BASE_TYPE_UNION) {
|
|
continue;
|
|
}
|
|
|
|
auto u = field->value.type.enum_def;
|
|
if (u->uses_multiple_type_instances) continue;
|
|
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
|
|
for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
|
|
auto& ev = **u_it;
|
|
if (ev.union_type.base_type == BASE_TYPE_NONE) {
|
|
continue;
|
|
}
|
|
|
|
auto full_struct_name = GetUnionElement(ev, false, opts_);
|
|
|
|
code_.SetValue(
|
|
"U_ELEMENT_TYPE",
|
|
WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
|
|
code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *");
|
|
code_.SetValue("U_ELEMENT_NAME", full_struct_name);
|
|
code_.SetValue("U_FIELD_NAME", Name(*field) + "_as_" + Name(ev));
|
|
|
|
// `template<> const T *union_name_as<T>() const` accessor.
|
|
// and `template<> T *mutable_union_name_as<T>()` accessor.
|
|
code_ +=
|
|
"template<> "
|
|
"inline {{U_FIELD_TYPE}}{{STRUCT_NAME}}::{{FIELD_NAME}}_as"
|
|
"<{{U_ELEMENT_NAME}}>() const {";
|
|
code_ += " return {{U_FIELD_NAME}}();";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
if (opts_.mutable_buffer) {
|
|
code_.SetValue("U_FIELD_TYPE", full_struct_name + " *");
|
|
code_.SetValue("U_FIELD_NAME",
|
|
"mutable_" + Name(*field) + "_as_" + Name(ev));
|
|
code_ +=
|
|
"template<> "
|
|
"inline {{U_FIELD_TYPE}}"
|
|
"{{STRUCT_NAME}}::mutable_{{FIELD_NAME}}_as"
|
|
"<{{U_ELEMENT_NAME}}>() {";
|
|
code_ += " return {{U_FIELD_NAME}}();";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
}
|
|
|
|
GenBuilders(struct_def);
|
|
|
|
if (opts_.generate_object_based_api) {
|
|
// Generate a pre-declaration for a CreateX method that works with an
|
|
// unpacked C++ object.
|
|
code_ += TableCreateSignature(struct_def, true, opts_) + ";";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
// Generate code to force vector alignment. Return empty string for vector
|
|
// that doesn't need alignment code.
|
|
std::string GenVectorForceAlign(const FieldDef& field,
|
|
const std::string& field_size) {
|
|
FLATBUFFERS_ASSERT(IsVector(field.value.type));
|
|
// Get the value of the force_align attribute.
|
|
const auto* force_align = field.attributes.Lookup("force_align");
|
|
const int align = force_align ? atoi(force_align->constant.c_str()) : 1;
|
|
// Generate code to do force_align for the vector.
|
|
if (align > 1) {
|
|
const auto vtype = field.value.type.VectorType();
|
|
const std::string& type =
|
|
IsStruct(vtype) ? WrapInNameSpace(*vtype.struct_def)
|
|
: GenTypeWire(vtype, "", false, field.offset64);
|
|
return std::string("_fbb.ForceVectorAlignment") +
|
|
(field.offset64 ? "64" : "") + "(" + field_size + ", sizeof(" +
|
|
type + "), " + std::to_string(static_cast<long long>(align)) +
|
|
");";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void GenBuilders(const StructDef& struct_def) {
|
|
code_.SetValue("STRUCT_NAME", Name(struct_def));
|
|
|
|
// Generate a builder struct:
|
|
code_ += "struct {{STRUCT_NAME}}Builder {";
|
|
code_ += " typedef {{STRUCT_NAME}} Table;";
|
|
code_ += " " + GetBuilder() + " &fbb_;";
|
|
code_ += " ::flatbuffers::uoffset_t start_;";
|
|
|
|
bool has_string_or_vector_fields = false;
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
const auto& field = **it;
|
|
if (field.deprecated) continue;
|
|
const bool is_scalar = IsScalar(field.value.type.base_type);
|
|
const bool is_default_scalar = is_scalar && !field.IsScalarOptional();
|
|
const bool is_string = IsString(field.value.type);
|
|
const bool is_vector = IsVector(field.value.type);
|
|
if (is_string || is_vector) {
|
|
has_string_or_vector_fields = true;
|
|
}
|
|
|
|
std::string offset = GenFieldOffsetName(field);
|
|
std::string name = GenUnderlyingCast(field, false, Name(field));
|
|
std::string value = is_default_scalar ? GenDefaultConstant(field) : "";
|
|
|
|
// Generate accessor functions of the form:
|
|
// void add_name(type name) {
|
|
// fbb_.AddElement<type>(offset, name, default);
|
|
// }
|
|
code_.SetValue("FIELD_NAME", Name(field));
|
|
code_.SetValue("FIELD_TYPE",
|
|
GenTypeWire(field.value.type, " ", true, field.offset64));
|
|
code_.SetValue("ADD_OFFSET", Name(struct_def) + "::" + offset);
|
|
code_.SetValue("ADD_NAME", name);
|
|
code_.SetValue("ADD_VALUE", value);
|
|
if (is_scalar) {
|
|
const auto type =
|
|
GenTypeWire(field.value.type, "", false, field.offset64);
|
|
code_.SetValue("ADD_FN", "AddElement<" + type + ">");
|
|
} else if (IsStruct(field.value.type)) {
|
|
code_.SetValue("ADD_FN", "AddStruct");
|
|
} else {
|
|
code_.SetValue("ADD_FN", "AddOffset");
|
|
}
|
|
|
|
code_ += " void add_{{FIELD_NAME}}({{FIELD_TYPE}}{{FIELD_NAME}}) {";
|
|
code_ += " fbb_.{{ADD_FN}}(\\";
|
|
if (is_default_scalar) {
|
|
code_ += "{{ADD_OFFSET}}, {{ADD_NAME}}, {{ADD_VALUE}});";
|
|
} else {
|
|
code_ += "{{ADD_OFFSET}}, {{ADD_NAME}});";
|
|
}
|
|
code_ += " }";
|
|
}
|
|
|
|
// Builder constructor
|
|
code_ += " explicit {{STRUCT_NAME}}Builder(" + GetBuilder() +
|
|
" "
|
|
"&_fbb)";
|
|
code_ += " : fbb_(_fbb) {";
|
|
code_ += " start_ = fbb_.StartTable();";
|
|
code_ += " }";
|
|
|
|
// Finish() function.
|
|
code_ += " ::flatbuffers::Offset<{{STRUCT_NAME}}> Finish() {";
|
|
code_ += " const auto end = fbb_.EndTable(start_);";
|
|
code_ += " auto o = ::flatbuffers::Offset<{{STRUCT_NAME}}>(end);";
|
|
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (!field->deprecated && field->IsRequired()) {
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
code_.SetValue("OFFSET_NAME", GenFieldOffsetName(*field));
|
|
code_ += " fbb_.Required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}});";
|
|
}
|
|
}
|
|
code_ += " return o;";
|
|
code_ += " }";
|
|
code_ += "};";
|
|
code_ += "";
|
|
|
|
// Generate a convenient CreateX function that uses the above builder
|
|
// to create a table in one go.
|
|
code_ +=
|
|
"inline ::flatbuffers::Offset<{{STRUCT_NAME}}> "
|
|
"Create{{STRUCT_NAME}}(";
|
|
code_ += " " + GetBuilder() + " &_fbb\\";
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (!field->deprecated) {
|
|
GenParam(*field, false, ",\n ");
|
|
}
|
|
}
|
|
code_ += ") {";
|
|
|
|
code_ += " {{STRUCT_NAME}}Builder builder_(_fbb);";
|
|
for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
|
|
size; size /= 2) {
|
|
for (auto it = struct_def.fields.vec.rbegin();
|
|
it != struct_def.fields.vec.rend(); ++it) {
|
|
const auto& field = **it;
|
|
if (!field.deprecated && (!struct_def.sortbysize ||
|
|
size == SizeOf(field.value.type.base_type))) {
|
|
code_.SetValue("FIELD_NAME", Name(field));
|
|
if (field.IsScalarOptional()) {
|
|
code_ +=
|
|
" if({{FIELD_NAME}}) { "
|
|
"builder_.add_{{FIELD_NAME}}(*{{FIELD_NAME}}); }";
|
|
} else {
|
|
code_ += " builder_.add_{{FIELD_NAME}}({{FIELD_NAME}});";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
code_ += " return builder_.Finish();";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
// Definition for type traits for this table type. This allows querying var-
|
|
// ious compile-time traits of the table.
|
|
if (opts_.g_cpp_std >= cpp::CPP_STD_17) {
|
|
GenTraitsStruct(struct_def);
|
|
}
|
|
|
|
// Generate a CreateXDirect function with vector types as parameters
|
|
if (opts_.cpp_direct_copy && has_string_or_vector_fields) {
|
|
code_ +=
|
|
"inline ::flatbuffers::Offset<{{STRUCT_NAME}}> "
|
|
"Create{{STRUCT_NAME}}Direct(";
|
|
code_ += " " + GetBuilder() + " &_fbb\\";
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (!field->deprecated) {
|
|
GenParam(*field, true, ",\n ");
|
|
}
|
|
}
|
|
// Need to call "Create" with the struct namespace.
|
|
const auto qualified_create_name =
|
|
struct_def.defined_namespace->GetFullyQualifiedName("Create");
|
|
code_.SetValue("CREATE_NAME", TranslateNameSpace(qualified_create_name));
|
|
code_ += ") {";
|
|
// Offset64 bit fields need to be added to the buffer first, so here we
|
|
// loop over the fields in order of their offset size, followed by their
|
|
// definition order. Otherwise the emitted code might add a Offset
|
|
// followed by an Offset64 which would trigger an assertion.
|
|
|
|
// TODO(derekbailey): maybe optimize for the case where there is no
|
|
// 64offsets in the whole schema?
|
|
ForAllFieldsOrderedByOffset(struct_def, [&](const FieldDef* field) {
|
|
if (field->deprecated) {
|
|
return;
|
|
}
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
if (IsString(field->value.type)) {
|
|
if (!field->shared) {
|
|
code_.SetValue(
|
|
"CREATE_STRING",
|
|
"CreateString" + std::string(field->offset64
|
|
? "<::flatbuffers::Offset64>"
|
|
: ""));
|
|
} else {
|
|
code_.SetValue("CREATE_STRING", "CreateSharedString");
|
|
}
|
|
if (field->value.constant != "0") {
|
|
std::string escaped;
|
|
flatbuffers::EscapeString(field->value.constant.c_str(),
|
|
field->value.constant.length(), &escaped,
|
|
true, false);
|
|
code_ +=
|
|
" auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
|
|
"_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : "
|
|
"_fbb.{{CREATE_STRING}}(" +
|
|
escaped + ");";
|
|
} else {
|
|
code_ +=
|
|
" auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
|
|
"_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;";
|
|
}
|
|
} else if (IsVector(field->value.type)) {
|
|
const std::string force_align_code =
|
|
GenVectorForceAlign(*field, Name(*field) + "->size()");
|
|
if (!force_align_code.empty()) {
|
|
code_ += " if ({{FIELD_NAME}}) { " + force_align_code + " }";
|
|
}
|
|
code_ += " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? \\";
|
|
const auto vtype = field->value.type.VectorType();
|
|
const auto has_key = TypeHasKey(vtype);
|
|
if (IsStruct(vtype)) {
|
|
const std::string type = WrapInNameSpace(*vtype.struct_def);
|
|
if (has_key) {
|
|
code_ += "_fbb.CreateVectorOfSortedStructs<" + type + ">\\";
|
|
} else {
|
|
// If the field uses 64-bit addressing, create a 64-bit vector.
|
|
if (field->value.type.base_type == BASE_TYPE_VECTOR64) {
|
|
code_ += "_fbb.CreateVectorOfStructs64\\";
|
|
} else {
|
|
code_ += "_fbb.CreateVectorOfStructs\\";
|
|
if (field->offset64) {
|
|
// This is normal 32-bit vector, with 64-bit addressing.
|
|
code_ += "64<::flatbuffers::Vector>\\";
|
|
} else {
|
|
code_ += "<" + type + ">\\";
|
|
}
|
|
}
|
|
}
|
|
} else if (has_key) {
|
|
const auto type = WrapInNameSpace(*vtype.struct_def);
|
|
code_ += "_fbb.CreateVectorOfSortedTables<" + type + ">\\";
|
|
} else {
|
|
const auto type = GenTypeWire(
|
|
vtype, "", VectorElementUserFacing(vtype), field->offset64);
|
|
|
|
if (field->value.type.base_type == BASE_TYPE_VECTOR64) {
|
|
code_ += "_fbb.CreateVector64\\";
|
|
} else {
|
|
// If the field uses 64-bit addressing, create a 64-bit vector.
|
|
code_.SetValue("64OFFSET", field->offset64 ? "64" : "");
|
|
code_.SetValue("TYPE",
|
|
field->offset64 ? "::flatbuffers::Vector" : type);
|
|
|
|
code_ += "_fbb.CreateVector{{64OFFSET}}<{{TYPE}}>\\";
|
|
}
|
|
}
|
|
code_ += has_key ? "({{FIELD_NAME}}) : 0;" : "(*{{FIELD_NAME}}) : 0;";
|
|
}
|
|
});
|
|
code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}(";
|
|
code_ += " _fbb\\";
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
continue;
|
|
}
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
code_ += ",\n {{FIELD_NAME}}\\";
|
|
if (IsString(field->value.type) || IsVector(field->value.type)) {
|
|
code_ += "__\\";
|
|
}
|
|
}
|
|
code_ += ");";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
|
|
std::string GenUnionUnpackVal(const FieldDef& afield,
|
|
const char* vec_elem_access,
|
|
const char* vec_type_access) {
|
|
auto type_name = WrapInNameSpace(*afield.value.type.enum_def);
|
|
return type_name + "Union::UnPack(" + "_e" + vec_elem_access + ", " +
|
|
EscapeKeyword(Name(afield) + UnionTypeFieldSuffix()) + "()" +
|
|
vec_type_access + ", _resolver)";
|
|
}
|
|
|
|
std::string GenUnpackVal(const Type& type, const std::string& val,
|
|
bool invector, const FieldDef& afield) {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_STRING: {
|
|
if (FlexibleStringConstructor(&afield)) {
|
|
return NativeString(&afield) + "(" + val + "->c_str(), " + val +
|
|
"->size())";
|
|
} else {
|
|
return val + "->str()";
|
|
}
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
if (IsStruct(type)) {
|
|
const auto& struct_attrs = type.struct_def->attributes;
|
|
const auto native_type = struct_attrs.Lookup("native_type");
|
|
if (native_type) {
|
|
std::string unpack_call = "::flatbuffers::UnPack";
|
|
const auto pack_name = struct_attrs.Lookup("native_type_pack_name");
|
|
if (pack_name) {
|
|
unpack_call += pack_name->constant;
|
|
}
|
|
unpack_call += "(*" + val + ")";
|
|
if (invector || afield.native_inline) {
|
|
return unpack_call;
|
|
} else {
|
|
const auto name = native_type->constant;
|
|
const auto ptype = GenTypeNativePtr(name, &afield, true);
|
|
return ptype + "(new " + name + "(" + unpack_call + "))";
|
|
}
|
|
} else if (invector || afield.native_inline) {
|
|
return "*" + val;
|
|
} else {
|
|
const auto name = WrapInNameSpace(*type.struct_def);
|
|
const auto ptype = GenTypeNativePtr(name, &afield, true);
|
|
return ptype + "(new " + name + "(*" + val + "))";
|
|
}
|
|
} else {
|
|
std::string ptype = afield.native_inline ? "*" : "";
|
|
ptype += GenTypeNativePtr(
|
|
WrapNativeNameInNameSpace(*type.struct_def, opts_), &afield,
|
|
true);
|
|
return ptype + "(" + val + "->UnPack(_resolver))";
|
|
}
|
|
}
|
|
case BASE_TYPE_UNION: {
|
|
return GenUnionUnpackVal(
|
|
afield, invector ? "->Get(_i)" : "",
|
|
invector ? ("->GetEnum<" + type.enum_def->name + ">(_i)").c_str()
|
|
: "");
|
|
}
|
|
default: {
|
|
return val;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string GenUnpackFieldStatement(const FieldDef& field,
|
|
const FieldDef* union_field) {
|
|
std::string code;
|
|
switch (field.value.type.base_type) {
|
|
case BASE_TYPE_VECTOR64:
|
|
case BASE_TYPE_VECTOR: {
|
|
auto name = Name(field);
|
|
if (field.value.type.element == BASE_TYPE_UTYPE) {
|
|
name = StripUnionType(Name(field));
|
|
}
|
|
const std::string vector_field = "_o->" + name;
|
|
code += "{ " + vector_field + ".resize(_e->size()); ";
|
|
if (!field.value.type.enum_def && !IsBool(field.value.type.element) &&
|
|
IsOneByte(field.value.type.element)) {
|
|
// For vectors of bytes, std::copy is used to improve performance.
|
|
// This doesn't work for:
|
|
// - enum types because they have to be explicitly static_cast.
|
|
// - vectors of bool, since they are a template specialization.
|
|
// - multiple-byte types due to endianness.
|
|
code +=
|
|
"std::copy(_e->begin(), _e->end(), _o->" + name + ".begin()); }";
|
|
} else {
|
|
std::string indexing;
|
|
if (field.value.type.enum_def) {
|
|
indexing += "static_cast<" +
|
|
WrapInNameSpace(*field.value.type.enum_def) + ">(";
|
|
}
|
|
indexing += "_e->Get(_i)";
|
|
if (field.value.type.enum_def) {
|
|
indexing += ")";
|
|
}
|
|
if (field.value.type.element == BASE_TYPE_BOOL) {
|
|
indexing += " != 0";
|
|
}
|
|
// Generate code that pushes data from _e to _o in the form:
|
|
// for (uoffset_t i = 0; i < _e->size(); ++i) {
|
|
// _o->field.push_back(_e->Get(_i));
|
|
// }
|
|
auto access =
|
|
field.value.type.element == BASE_TYPE_UTYPE
|
|
? ".type"
|
|
: (field.value.type.element == BASE_TYPE_UNION ? ".value"
|
|
: "");
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
|
code += "for (::flatbuffers::uoffset64_t _i = 0;";
|
|
} else {
|
|
code += "for (::flatbuffers::uoffset_t _i = 0;";
|
|
}
|
|
code += " _i < _e->size(); _i++) { ";
|
|
auto cpp_type = field.attributes.Lookup("cpp_type");
|
|
if (cpp_type) {
|
|
// Generate code that resolves the cpp pointer type, of the form:
|
|
// if (resolver)
|
|
// (*resolver)(&_o->field, (hash_value_t)(_e));
|
|
// else
|
|
// _o->field = nullptr;
|
|
code += "/*vector resolver, " + PtrType(&field) + "*/ ";
|
|
code += "if (_resolver) ";
|
|
code += "(*_resolver)";
|
|
code += "(reinterpret_cast<void **>(&_o->" + name + "[_i]" +
|
|
access + "), ";
|
|
code +=
|
|
"static_cast<::flatbuffers::hash_value_t>(" + indexing + "));";
|
|
if (PtrType(&field) == "naked") {
|
|
code += " else ";
|
|
code += "_o->" + name + "[_i]" + access + " = nullptr; ";
|
|
} else {
|
|
// code += " else ";
|
|
// code += "_o->" + name + "[_i]" + access + " = " +
|
|
// GenTypeNativePtr(cpp_type->constant, &field, true) + "();";
|
|
code += "/* else do nothing */";
|
|
}
|
|
} else {
|
|
const bool is_pointer = IsVectorOfPointers(field);
|
|
if (is_pointer) {
|
|
code += "if(_o->" + name + "[_i]" + ") { ";
|
|
code += indexing + "->UnPackTo(_o->" + name + "[_i]" +
|
|
GenPtrGet(field) + ", _resolver);";
|
|
code += " } else { ";
|
|
}
|
|
code += "_o->" + name + "[_i]" + access + " = ";
|
|
code += GenUnpackVal(field.value.type.VectorType(), indexing, true,
|
|
field);
|
|
code += "; ";
|
|
if (is_pointer) {
|
|
code += "} ";
|
|
}
|
|
}
|
|
code += "} } else { " + vector_field + ".resize(0); }";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_UTYPE: {
|
|
FLATBUFFERS_ASSERT(union_field->value.type.base_type ==
|
|
BASE_TYPE_UNION);
|
|
// Generate code that sets the union type, of the form:
|
|
// _o->field.type = _e;
|
|
code += "_o->" + Name(*union_field) + ".type = _e;";
|
|
break;
|
|
}
|
|
case BASE_TYPE_UNION: {
|
|
// Generate code that sets the union value, of the form:
|
|
// _o->field.value = Union::Unpack(_e, field_type(), resolver);
|
|
code += "_o->" + Name(field) + ".value = ";
|
|
code += GenUnionUnpackVal(field, "", "");
|
|
code += ";";
|
|
break;
|
|
}
|
|
default: {
|
|
auto cpp_type = field.attributes.Lookup("cpp_type");
|
|
if (cpp_type) {
|
|
// Generate code that resolves the cpp pointer type, of the form:
|
|
// if (resolver)
|
|
// (*resolver)(&_o->field, (hash_value_t)(_e));
|
|
// else
|
|
// _o->field = nullptr;
|
|
code += "/*scalar resolver, " + PtrType(&field) + "*/ ";
|
|
code += "if (_resolver) ";
|
|
code += "(*_resolver)";
|
|
code += "(reinterpret_cast<void **>(&_o->" + Name(field) + "), ";
|
|
code += "static_cast<::flatbuffers::hash_value_t>(_e));";
|
|
if (PtrType(&field) == "naked") {
|
|
code += " else ";
|
|
code += "_o->" + Name(field) + " = nullptr;";
|
|
} else {
|
|
// code += " else ";
|
|
// code += "_o->" + Name(field) + " = " +
|
|
// GenTypeNativePtr(cpp_type->constant, &field, true) + "();";
|
|
code += "/* else do nothing */;";
|
|
}
|
|
} else {
|
|
// Generate code for assigning the value, of the form:
|
|
// _o->field = value;
|
|
const bool is_pointer = IsPointer(field);
|
|
|
|
const std::string out_field = "_o->" + Name(field);
|
|
|
|
if (is_pointer) {
|
|
code += "{ if(" + out_field + ") { ";
|
|
code += "_e->UnPackTo(" + out_field + GenPtrGet(field) +
|
|
", _resolver);";
|
|
code += " } else { ";
|
|
}
|
|
code += out_field + " = ";
|
|
code += GenUnpackVal(field.value.type, "_e", false, field) + ";";
|
|
if (is_pointer) {
|
|
code += " } } else if (" + out_field + ") { " + out_field +
|
|
".reset(); }";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return code;
|
|
}
|
|
|
|
std::string GenCreateParam(const FieldDef& field) {
|
|
std::string value = "_o->";
|
|
if (field.value.type.base_type == BASE_TYPE_UTYPE) {
|
|
value += StripUnionType(Name(field));
|
|
value += ".type";
|
|
} else {
|
|
value += Name(field);
|
|
}
|
|
if (!IsVector(field.value.type) && field.attributes.Lookup("cpp_type")) {
|
|
auto type = GenTypeBasic(field.value.type, false);
|
|
value =
|
|
"_rehasher ? "
|
|
"static_cast<" +
|
|
type + ">((*_rehasher)(" + value + GenPtrGet(field) + ")) : 0";
|
|
}
|
|
|
|
std::string code;
|
|
switch (field.value.type.base_type) {
|
|
// String fields are of the form:
|
|
// _fbb.CreateString(_o->field)
|
|
// or
|
|
// _fbb.CreateSharedString(_o->field)
|
|
case BASE_TYPE_STRING: {
|
|
if (!field.shared) {
|
|
code +=
|
|
"_fbb.CreateString" +
|
|
std::string(field.offset64 ? "<::flatbuffers::Offset64>" : "") +
|
|
"(";
|
|
} else {
|
|
code += "_fbb.CreateSharedString(";
|
|
}
|
|
code += value;
|
|
code.push_back(')');
|
|
|
|
// For optional fields, check to see if there actually is any data
|
|
// in _o->field before attempting to access it. If there isn't,
|
|
// depending on set_empty_strings_to_null either set it to 0 or an empty
|
|
// string.
|
|
if (!field.IsRequired()) {
|
|
auto empty_value = opts_.set_empty_strings_to_null
|
|
? "0"
|
|
: "_fbb.CreateSharedString(\"\")";
|
|
code = value + ".empty() ? " + empty_value + " : " + code;
|
|
}
|
|
break;
|
|
}
|
|
// Vector fields come in several flavours, of the forms:
|
|
// _fbb.CreateVector(_o->field);
|
|
// _fbb.CreateVector((const utype*)_o->field.data(),
|
|
// _o->field.size()); _fbb.CreateVectorOfStrings(_o->field)
|
|
// _fbb.CreateVectorOfStructs(_o->field)
|
|
// _fbb.CreateVector<Offset<T>>(_o->field.size() [&](size_t i) {
|
|
// return CreateT(_fbb, _o->Get(i), rehasher);
|
|
// });
|
|
case BASE_TYPE_VECTOR64:
|
|
case BASE_TYPE_VECTOR: {
|
|
auto vector_type = field.value.type.VectorType();
|
|
switch (vector_type.base_type) {
|
|
case BASE_TYPE_STRING: {
|
|
if (NativeString(&field) == "std::string") {
|
|
code += "_fbb.CreateVectorOfStrings(" + value + ")";
|
|
} else {
|
|
// Use by-function serialization to emulate
|
|
// CreateVectorOfStrings(); this works also with non-std strings.
|
|
code +=
|
|
"_fbb.CreateVector<::flatbuffers::Offset<::flatbuffers::"
|
|
"String>>"
|
|
" ";
|
|
code += "(" + value + ".size(), ";
|
|
code += "[](size_t i, _VectorArgs *__va) { ";
|
|
code +=
|
|
"return __va->__fbb->CreateString(__va->_" + value + "[i]);";
|
|
code += " }, &_va )";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
if (IsStruct(vector_type)) {
|
|
const auto& struct_attrs =
|
|
field.value.type.struct_def->attributes;
|
|
const auto native_type = struct_attrs.Lookup("native_type");
|
|
if (native_type) {
|
|
code += "_fbb.CreateVectorOfNativeStructs<";
|
|
code += WrapInNameSpace(*vector_type.struct_def) + ", " +
|
|
native_type->constant + ">";
|
|
code += "(" + value;
|
|
const auto pack_name =
|
|
struct_attrs.Lookup("native_type_pack_name");
|
|
if (pack_name) {
|
|
code += ", ::flatbuffers::Pack" + pack_name->constant;
|
|
}
|
|
code += ")";
|
|
} else {
|
|
// If the field uses 64-bit addressing, create a 64-bit vector.
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
|
code += "_fbb.CreateVectorOfStructs64";
|
|
} else {
|
|
code += "_fbb.CreateVectorOfStructs";
|
|
if (field.offset64) {
|
|
// This is normal 32-bit vector, with 64-bit addressing.
|
|
code += "64<::flatbuffers::Vector>";
|
|
}
|
|
}
|
|
code += "(" + value + ")";
|
|
}
|
|
} else {
|
|
code += "_fbb.CreateVector<::flatbuffers::Offset<";
|
|
code += WrapInNameSpace(*vector_type.struct_def) + ">> ";
|
|
code += "(" + value + ".size(), ";
|
|
code += "[](size_t i, _VectorArgs *__va) { ";
|
|
code += "return " +
|
|
WrapInNameSpace(vector_type.struct_def->defined_namespace,
|
|
"Create" + vector_type.struct_def->name);
|
|
code += "(*__va->__fbb, ";
|
|
if (field.native_inline) {
|
|
code += "&(__va->_" + value + "[i])";
|
|
} else {
|
|
code += "__va->_" + value + "[i]" + GenPtrGet(field);
|
|
}
|
|
code += ", __va->__rehasher); }, &_va )";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_BOOL: {
|
|
code += "_fbb.CreateVector(" + value + ")";
|
|
break;
|
|
}
|
|
case BASE_TYPE_UNION: {
|
|
code +=
|
|
"_fbb.CreateVector<::flatbuffers::"
|
|
"Offset<void>>(" +
|
|
value +
|
|
".size(), [](size_t i, _VectorArgs *__va) { "
|
|
"return __va->_" +
|
|
value + "[i].Pack(*__va->__fbb, __va->__rehasher); }, &_va)";
|
|
break;
|
|
}
|
|
case BASE_TYPE_UTYPE: {
|
|
value = StripUnionType(value);
|
|
auto underlying_type = GenTypeBasic(vector_type, false);
|
|
const std::string& type = opts_.scoped_enums
|
|
? Name(*field.value.type.enum_def)
|
|
: underlying_type;
|
|
auto enum_value = "__va->_" + value + "[i].type";
|
|
if (!opts_.scoped_enums)
|
|
enum_value =
|
|
"static_cast<" + underlying_type + ">(" + enum_value + ")";
|
|
|
|
code += "_fbb.CreateVector<" + type + ">(" + value +
|
|
".size(), [](size_t i, _VectorArgs *__va) { return " +
|
|
enum_value + "; }, &_va)";
|
|
break;
|
|
}
|
|
default: {
|
|
if (field.value.type.enum_def &&
|
|
!VectorElementUserFacing(vector_type)) {
|
|
// For enumerations, we need to get access to the array data for
|
|
// the underlying storage type (eg. uint8_t).
|
|
const auto basetype = GenTypeBasic(
|
|
field.value.type.enum_def->underlying_type, false);
|
|
code += "_fbb.CreateVectorScalarCast<" + basetype +
|
|
">(::flatbuffers::data(" + value + "), " + value +
|
|
".size())";
|
|
} else if (field.attributes.Lookup("cpp_type")) {
|
|
auto type = GenTypeBasic(vector_type, false);
|
|
code += "_fbb.CreateVector<" + type + ">(" + value + ".size(), ";
|
|
code += "[](size_t i, _VectorArgs *__va) { ";
|
|
code += "return __va->__rehasher ? ";
|
|
code += "static_cast<" + type + ">((*__va->__rehasher)";
|
|
code += "(__va->_" + value + "[i]" + GenPtrGet(field) + ")) : 0";
|
|
code += "; }, &_va )";
|
|
} else {
|
|
// If the field uses 64-bit addressing, create a 64-bit vector.
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR64) {
|
|
code += "_fbb.CreateVector64(" + value + ")";
|
|
} else {
|
|
code += "_fbb.CreateVector";
|
|
if (field.offset64) {
|
|
// This is normal 32-bit vector, with 64-bit addressing.
|
|
code += "64<::flatbuffers::Vector>";
|
|
}
|
|
code += "(" + value + ")";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If set_empty_vectors_to_null option is enabled, for optional fields,
|
|
// check to see if there actually is any data in _o->field before
|
|
// attempting to access it.
|
|
if (field.attributes.Lookup("nested_flatbuffer") ||
|
|
(opts_.set_empty_vectors_to_null && !field.IsRequired())) {
|
|
code = value + ".size() ? " + code + " : 0";
|
|
}
|
|
break;
|
|
}
|
|
case BASE_TYPE_UNION: {
|
|
// _o->field.Pack(_fbb);
|
|
code += value + ".Pack(_fbb)";
|
|
break;
|
|
}
|
|
case BASE_TYPE_STRUCT: {
|
|
if (IsStruct(field.value.type)) {
|
|
const auto& struct_attribs = field.value.type.struct_def->attributes;
|
|
const auto native_type = struct_attribs.Lookup("native_type");
|
|
if (native_type && field.native_inline) {
|
|
code += "::flatbuffers::Pack";
|
|
const auto pack_name =
|
|
struct_attribs.Lookup("native_type_pack_name");
|
|
if (pack_name) {
|
|
code += pack_name->constant;
|
|
}
|
|
code += "(" + value + ")";
|
|
} else if (native_type && !field.native_inline) {
|
|
code += WrapInNameSpace(*field.value.type.struct_def) + "{};";
|
|
code += " if (_o->" + Name(field) + ") _" + Name(field) +
|
|
" = ::flatbuffers::Pack(*_o->" + Name(field) + ")";
|
|
} else if (field.native_inline) {
|
|
code += "&" + value;
|
|
} else {
|
|
code += value + " ? " + value + GenPtrGet(field) + " : nullptr";
|
|
}
|
|
} else {
|
|
// _o->field ? CreateT(_fbb, _o->field.get(), _rehasher);
|
|
const auto& nested_struct = *field.value.type.struct_def;
|
|
code += value + " ? " +
|
|
WrapInNameSpace(nested_struct.defined_namespace,
|
|
"Create" + nested_struct.name);
|
|
code += "(_fbb, " + value;
|
|
if (!field.native_inline) code += GenPtrGet(field);
|
|
code += ", _rehasher) : 0";
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
code += value;
|
|
break;
|
|
}
|
|
}
|
|
return code;
|
|
}
|
|
|
|
// Generate code for tables that needs to come after the regular definition.
|
|
void GenTablePost(const StructDef& struct_def) {
|
|
// Don't generate an object API struct for the table if it is a native type.
|
|
const auto native_type = struct_def.attributes.Lookup("native_type");
|
|
if (opts_.generate_object_based_api && !native_type) {
|
|
GenNativeTablePost(struct_def);
|
|
}
|
|
|
|
code_.SetValue("STRUCT_NAME", Name(struct_def));
|
|
code_.SetValue("NATIVE_NAME",
|
|
NativeName(Name(struct_def), &struct_def, opts_));
|
|
|
|
if (opts_.generate_object_based_api) {
|
|
// Generate the >= C++11 copy ctor and assignment operator definitions.
|
|
if (!native_type) {
|
|
GenCopyCtorAssignOpDefs(struct_def);
|
|
}
|
|
|
|
// Generate the X::UnPack() method.
|
|
code_ +=
|
|
"inline " + TableUnPackSignature(struct_def, false, opts_) + " {";
|
|
|
|
if (opts_.g_cpp_std == cpp::CPP_STD_X0) {
|
|
auto native_name = WrapNativeNameInNameSpace(struct_def, parser_.opts);
|
|
code_.SetValue("POINTER_TYPE",
|
|
GenTypeNativePtr(native_name, nullptr, false));
|
|
code_ +=
|
|
" {{POINTER_TYPE}} _o = {{POINTER_TYPE}}(new {{NATIVE_NAME}}());";
|
|
} else if (opts_.g_cpp_std == cpp::CPP_STD_11) {
|
|
code_ +=
|
|
" auto _o = std::unique_ptr<{{NATIVE_NAME}}>(new "
|
|
"{{NATIVE_NAME}}());";
|
|
} else {
|
|
code_ += " auto _o = std::make_unique<{{NATIVE_NAME}}>();";
|
|
}
|
|
code_ += " UnPackTo(_o.get(), _resolver);";
|
|
code_ += " return _o.release();";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
// Generate an Unpack method for the C++ object if that table does not
|
|
// have a native type.
|
|
if (!native_type) {
|
|
code_ +=
|
|
"inline " + TableUnPackToSignature(struct_def, false, opts_) + " {";
|
|
code_ += " (void)_o;";
|
|
code_ += " (void)_resolver;";
|
|
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
const auto& field = **it;
|
|
if (field.deprecated) {
|
|
continue;
|
|
}
|
|
|
|
// Assign a value from |this| to |_o|. Values from |this| are stored
|
|
// in a variable |_e| by calling this->field_type(). The value is
|
|
// then assigned to |_o| using the GenUnpackFieldStatement.
|
|
const bool is_union = field.value.type.base_type == BASE_TYPE_UTYPE;
|
|
const auto statement =
|
|
GenUnpackFieldStatement(field, is_union ? *(it + 1) : nullptr);
|
|
|
|
code_.SetValue("FIELD_NAME", Name(field));
|
|
auto prefix = " { auto _e = {{FIELD_NAME}}(); ";
|
|
auto check = IsScalar(field.value.type.base_type) ? "" : "if (_e) ";
|
|
auto postfix = " }";
|
|
code_ += std::string(prefix) + check + statement + postfix;
|
|
}
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
|
|
// Generate the global CreateX function that simply calls the
|
|
// X::Pack member function.
|
|
code_ +=
|
|
"inline " + TableCreateSignature(struct_def, false, opts_) + " {";
|
|
code_ += " return {{STRUCT_NAME}}::Pack(_fbb, _o, _rehasher);";
|
|
code_ += "}";
|
|
code_ += "";
|
|
|
|
if (!native_type) {
|
|
// Generate a Pack method that works with an unpacked C++ object if it
|
|
// does not have a native type.
|
|
code_ +=
|
|
"inline " + TablePackSignature(struct_def, false, opts_) + " {";
|
|
code_ += " (void)_rehasher;";
|
|
code_ += " (void)_o;";
|
|
|
|
code_ +=
|
|
" struct _VectorArgs "
|
|
"{ " +
|
|
GetBuilder() +
|
|
" *__fbb; "
|
|
"const " +
|
|
NativeName(Name(struct_def), &struct_def, opts_) +
|
|
"* __o; "
|
|
"const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { "
|
|
"&_fbb, _o, _rehasher}; (void)_va;";
|
|
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
auto& field = **it;
|
|
if (field.deprecated) {
|
|
continue;
|
|
}
|
|
if (IsVector(field.value.type)) {
|
|
const std::string force_align_code =
|
|
GenVectorForceAlign(field, "_o->" + Name(field) + ".size()");
|
|
if (!force_align_code.empty()) {
|
|
code_ += " " + force_align_code;
|
|
}
|
|
}
|
|
code_ +=
|
|
" auto _" + Name(field) + " = " + GenCreateParam(field) + ";";
|
|
}
|
|
// Need to call "Create" with the struct namespace.
|
|
const auto qualified_create_name =
|
|
struct_def.defined_namespace->GetFullyQualifiedName("Create");
|
|
code_.SetValue("CREATE_NAME",
|
|
TranslateNameSpace(qualified_create_name));
|
|
|
|
code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}(";
|
|
code_ += " _fbb\\";
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
if (field->deprecated) {
|
|
continue;
|
|
}
|
|
|
|
bool pass_by_address = false;
|
|
bool check_ptr = false;
|
|
if (field->value.type.base_type == BASE_TYPE_STRUCT) {
|
|
if (IsStruct(field->value.type)) {
|
|
auto native_type =
|
|
field->value.type.struct_def->attributes.Lookup(
|
|
"native_type");
|
|
auto native_inline = field->attributes.Lookup("native_inline");
|
|
if (native_type) {
|
|
pass_by_address = true;
|
|
}
|
|
if (native_type && !native_inline) {
|
|
check_ptr = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the CreateX function using values from |_o|.
|
|
if (pass_by_address && check_ptr) {
|
|
code_ += ",\n _o->" + Name(*field) + " ? &_" + Name(*field) +
|
|
" : nullptr\\";
|
|
} else if (pass_by_address) {
|
|
code_ += ",\n &_" + Name(*field) + "\\";
|
|
} else {
|
|
code_ += ",\n _" + Name(*field) + "\\";
|
|
}
|
|
}
|
|
code_ += ");";
|
|
code_ += "}";
|
|
code_ += "";
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GenPadding(
|
|
const FieldDef& field, std::string* code_ptr, int* id,
|
|
const std::function<void(int bits, std::string* code_ptr, int* id)>& f) {
|
|
if (field.padding) {
|
|
for (int i = 0; i < 4; i++) {
|
|
if (static_cast<int>(field.padding) & (1 << i)) {
|
|
f((1 << i) * 8, code_ptr, id);
|
|
}
|
|
}
|
|
FLATBUFFERS_ASSERT(!(field.padding & ~0xF));
|
|
}
|
|
}
|
|
|
|
static void PaddingDefinition(int bits, std::string* code_ptr, int* id) {
|
|
*code_ptr += " int" + NumToString(bits) + "_t padding" +
|
|
NumToString((*id)++) + "__;";
|
|
}
|
|
|
|
static void PaddingInitializer(int bits, std::string* code_ptr, int* id) {
|
|
(void)bits;
|
|
if (!code_ptr->empty()) *code_ptr += ",\n ";
|
|
*code_ptr += "padding" + NumToString((*id)++) + "__(0)";
|
|
}
|
|
|
|
static void PaddingNoop(int bits, std::string* code_ptr, int* id) {
|
|
(void)bits;
|
|
if (!code_ptr->empty()) *code_ptr += '\n';
|
|
*code_ptr += " (void)padding" + NumToString((*id)++) + "__;";
|
|
}
|
|
|
|
void GenStructDefaultConstructor(const StructDef& struct_def) {
|
|
std::string init_list;
|
|
std::string body;
|
|
bool first_in_init_list = true;
|
|
int padding_initializer_id = 0;
|
|
int padding_body_id = 0;
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
const auto field_name = Name(*field) + "_";
|
|
|
|
if (first_in_init_list) {
|
|
first_in_init_list = false;
|
|
} else {
|
|
init_list += ",";
|
|
init_list += "\n ";
|
|
}
|
|
|
|
init_list += field_name;
|
|
if (IsStruct(field->value.type) || IsArray(field->value.type)) {
|
|
// this is either default initialization of struct
|
|
// or
|
|
// implicit initialization of array
|
|
// for each object in array it:
|
|
// * sets it as zeros for POD types (integral, floating point, etc)
|
|
// * calls default constructor for classes/structs
|
|
init_list += "()";
|
|
} else {
|
|
init_list += "(0)";
|
|
}
|
|
if (field->padding) {
|
|
GenPadding(*field, &init_list, &padding_initializer_id,
|
|
PaddingInitializer);
|
|
GenPadding(*field, &body, &padding_body_id, PaddingNoop);
|
|
}
|
|
}
|
|
|
|
if (init_list.empty()) {
|
|
code_ += " {{STRUCT_NAME}}()";
|
|
code_ += " {}";
|
|
} else {
|
|
code_.SetValue("INIT_LIST", init_list);
|
|
code_ += " {{STRUCT_NAME}}()";
|
|
code_ += " : {{INIT_LIST}} {";
|
|
if (!body.empty()) {
|
|
code_ += body;
|
|
}
|
|
code_ += " }";
|
|
}
|
|
}
|
|
|
|
void GenStructConstructor(const StructDef& struct_def,
|
|
GenArrayArgMode array_mode) {
|
|
std::string arg_list;
|
|
std::string init_list;
|
|
int padding_id = 0;
|
|
auto first = struct_def.fields.vec.begin();
|
|
// skip arrays if generate ctor without array assignment
|
|
const auto init_arrays = (array_mode != kArrayArgModeNone);
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
const auto& field = **it;
|
|
const auto& type = field.value.type;
|
|
const auto is_array = IsArray(type);
|
|
const auto arg_name = "_" + Name(field);
|
|
if (!is_array || init_arrays) {
|
|
if (it != first && !arg_list.empty()) {
|
|
arg_list += ", ";
|
|
}
|
|
arg_list += !is_array ? GenTypeGet(type, " ", "const ", " &", true)
|
|
: GenTypeSpan(type, true, type.fixed_length);
|
|
arg_list += arg_name;
|
|
}
|
|
// skip an array with initialization from span
|
|
if (false == (is_array && init_arrays)) {
|
|
if (it != first && !init_list.empty()) {
|
|
init_list += ",\n ";
|
|
}
|
|
init_list += Name(field) + "_";
|
|
if (IsScalar(type.base_type)) {
|
|
auto scalar_type = GenUnderlyingCast(field, false, arg_name);
|
|
init_list += "(::flatbuffers::EndianScalar(" + scalar_type + "))";
|
|
} else {
|
|
FLATBUFFERS_ASSERT((is_array && !init_arrays) || IsStruct(type));
|
|
if (!is_array)
|
|
init_list += "(" + arg_name + ")";
|
|
else
|
|
init_list += "()";
|
|
}
|
|
}
|
|
if (field.padding)
|
|
GenPadding(field, &init_list, &padding_id, PaddingInitializer);
|
|
}
|
|
|
|
if (!arg_list.empty()) {
|
|
code_.SetValue("ARG_LIST", arg_list);
|
|
code_.SetValue("INIT_LIST", init_list);
|
|
if (!init_list.empty()) {
|
|
code_ += " {{STRUCT_NAME}}({{ARG_LIST}})";
|
|
code_ += " : {{INIT_LIST}} {";
|
|
} else {
|
|
code_ += " {{STRUCT_NAME}}({{ARG_LIST}}) {";
|
|
}
|
|
padding_id = 0;
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
const auto& type = field->value.type;
|
|
if (IsArray(type) && init_arrays) {
|
|
const auto& element_type = type.VectorType();
|
|
const auto is_enum = IsEnum(element_type);
|
|
FLATBUFFERS_ASSERT(
|
|
(IsScalar(element_type.base_type) || IsStruct(element_type)) &&
|
|
"invalid declaration");
|
|
const auto face_type = GenTypeGet(type, " ", "", "", is_enum);
|
|
std::string get_array =
|
|
is_enum ? "CastToArrayOfEnum<" + face_type + ">" : "CastToArray";
|
|
const auto field_name = Name(*field) + "_";
|
|
const auto arg_name = "_" + Name(*field);
|
|
code_ += " ::flatbuffers::" + get_array + "(" + field_name +
|
|
").CopyFromSpan(" + arg_name + ");";
|
|
}
|
|
if (field->padding) {
|
|
std::string padding;
|
|
GenPadding(*field, &padding, &padding_id, PaddingNoop);
|
|
code_ += padding;
|
|
}
|
|
}
|
|
code_ += " }";
|
|
}
|
|
}
|
|
|
|
void GenArrayAccessor(const Type& type, bool mutable_accessor) {
|
|
FLATBUFFERS_ASSERT(IsArray(type));
|
|
const auto is_enum = IsEnum(type.VectorType());
|
|
// The Array<bool,N> is a tricky case, like std::vector<bool>.
|
|
// It requires a specialization of Array class.
|
|
// Generate Array<uint8_t> for Array<bool>.
|
|
const auto face_type = GenTypeGet(type, " ", "", "", is_enum);
|
|
std::string ret_type = "::flatbuffers::Array<" + face_type + ", " +
|
|
NumToString(type.fixed_length) + ">";
|
|
if (mutable_accessor)
|
|
code_ += " " + ret_type + " *mutable_{{FIELD_NAME}}() {";
|
|
else
|
|
code_ += " const " + ret_type + " *{{FIELD_NAME}}() const {";
|
|
|
|
std::string get_array =
|
|
is_enum ? "CastToArrayOfEnum<" + face_type + ">" : "CastToArray";
|
|
code_ += " return &::flatbuffers::" + get_array + "({{FIELD_VALUE}});";
|
|
code_ += " }";
|
|
}
|
|
|
|
// Generate an accessor struct with constructor for a flatbuffers struct.
|
|
void GenStruct(const StructDef& struct_def) {
|
|
// Generate an accessor struct, with private variables of the form:
|
|
// type name_;
|
|
// Generates manual padding and alignment.
|
|
// Variables are private because they contain little endian data on all
|
|
// platforms.
|
|
GenComment(struct_def.doc_comment);
|
|
code_.SetValue("ALIGN", NumToString(struct_def.minalign));
|
|
code_.SetValue("STRUCT_NAME", Name(struct_def));
|
|
|
|
code_ +=
|
|
"FLATBUFFERS_MANUALLY_ALIGNED_STRUCT({{ALIGN}}) "
|
|
"{{STRUCT_NAME}} FLATBUFFERS_FINAL_CLASS {";
|
|
code_ += " private:";
|
|
|
|
int padding_id = 0;
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
const auto& field_type = field->value.type;
|
|
code_.SetValue("FIELD_TYPE", GenTypeGet(field_type, " ", "", " ", false));
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
code_.SetValue("ARRAY",
|
|
IsArray(field_type)
|
|
? "[" + NumToString(field_type.fixed_length) + "]"
|
|
: "");
|
|
code_ += (" {{FIELD_TYPE}}{{FIELD_NAME}}_{{ARRAY}};");
|
|
|
|
if (field->padding) {
|
|
std::string padding;
|
|
GenPadding(*field, &padding, &padding_id, PaddingDefinition);
|
|
code_ += padding;
|
|
}
|
|
}
|
|
|
|
// Generate GetFullyQualifiedName
|
|
code_ += "";
|
|
code_ += " public:";
|
|
|
|
if (opts_.g_cpp_std >= cpp::CPP_STD_17) {
|
|
code_ += " struct Traits;";
|
|
}
|
|
|
|
// Make TypeTable accessible via the generated struct.
|
|
if (opts_.mini_reflect != IDLOptions::kNone) {
|
|
code_ +=
|
|
" static const ::flatbuffers::TypeTable *MiniReflectTypeTable() {";
|
|
code_ += " return {{STRUCT_NAME}}TypeTable();";
|
|
code_ += " }";
|
|
}
|
|
|
|
GenFullyQualifiedNameGetter(struct_def, Name(struct_def));
|
|
|
|
// Generate a default constructor.
|
|
GenStructDefaultConstructor(struct_def);
|
|
|
|
// Generate a constructor that takes all fields as arguments,
|
|
// excluding arrays.
|
|
GenStructConstructor(struct_def, kArrayArgModeNone);
|
|
|
|
auto arrays_num = std::count_if(
|
|
struct_def.fields.vec.begin(), struct_def.fields.vec.end(),
|
|
[](const FieldDef* fd) { return IsArray(fd->value.type); });
|
|
if (arrays_num > 0) {
|
|
GenStructConstructor(struct_def, kArrayArgModeSpanStatic);
|
|
}
|
|
|
|
// Generate accessor methods of the form:
|
|
// type name() const { return ::flatbuffers::EndianScalar(name_); }
|
|
for (const auto& field : struct_def.fields.vec) {
|
|
const auto& type = field->value.type;
|
|
const auto is_scalar = IsScalar(type.base_type);
|
|
const auto is_array = IsArray(type);
|
|
|
|
const auto field_type = GenTypeGet(type, " ", is_array ? "" : "const ",
|
|
is_array ? "" : " &", true);
|
|
auto member = Name(*field) + "_";
|
|
const std::string& value =
|
|
is_scalar ? "::flatbuffers::EndianScalar(" + member + ")" : member;
|
|
|
|
code_.SetValue("FIELD_NAME", Name(*field));
|
|
code_.SetValue("FIELD_TYPE", field_type);
|
|
code_.SetValue("FIELD_VALUE", GenUnderlyingCast(*field, true, value));
|
|
|
|
GenComment(field->doc_comment, " ");
|
|
|
|
// Generate a const accessor function.
|
|
if (is_array) {
|
|
GenArrayAccessor(type, false);
|
|
} else {
|
|
code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
|
|
code_ += " return {{FIELD_VALUE}};";
|
|
code_ += " }";
|
|
}
|
|
|
|
// Generate a mutable accessor function.
|
|
if (opts_.mutable_buffer) {
|
|
auto mut_field_type =
|
|
GenTypeGet(type, " ", "", is_array ? "" : " &", true);
|
|
code_.SetValue("FIELD_TYPE", mut_field_type);
|
|
if (is_scalar) {
|
|
code_.SetValue("ARG", GenTypeBasic(type, true));
|
|
code_.SetValue("FIELD_VALUE",
|
|
GenUnderlyingCast(*field, false, "_" + Name(*field)));
|
|
|
|
code_ += " void mutate_{{FIELD_NAME}}({{ARG}} _{{FIELD_NAME}}) {";
|
|
code_ +=
|
|
" ::flatbuffers::WriteScalar(&{{FIELD_NAME}}_, "
|
|
"{{FIELD_VALUE}});";
|
|
code_ += " }";
|
|
} else if (is_array) {
|
|
GenArrayAccessor(type, true);
|
|
} else {
|
|
code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
|
|
code_ += " return {{FIELD_VALUE}};";
|
|
code_ += " }";
|
|
}
|
|
}
|
|
|
|
// Generate a comparison function for this field if it is a key.
|
|
if (field->key) {
|
|
GenKeyFieldMethods(*field);
|
|
}
|
|
}
|
|
code_.SetValue("NATIVE_NAME", Name(struct_def));
|
|
GenOperatorNewDelete(struct_def);
|
|
|
|
if (opts_.cpp_static_reflection) {
|
|
GenIndexBasedFieldGetter(struct_def);
|
|
}
|
|
|
|
code_ += "};";
|
|
|
|
code_.SetValue("STRUCT_BYTE_SIZE", NumToString(struct_def.bytesize));
|
|
code_ += "FLATBUFFERS_STRUCT_END({{STRUCT_NAME}}, {{STRUCT_BYTE_SIZE}});";
|
|
if (opts_.gen_compare) {
|
|
GenCompareOperator(struct_def, "()");
|
|
}
|
|
if (opts_.gen_absl_hash) {
|
|
GenAbslHashValue(struct_def);
|
|
}
|
|
code_ += "";
|
|
|
|
// Definition for type traits for this table type. This allows querying var-
|
|
// ious compile-time traits of the table.
|
|
if (opts_.g_cpp_std >= cpp::CPP_STD_17) {
|
|
GenTraitsStruct(struct_def);
|
|
}
|
|
}
|
|
|
|
// Set up the correct namespace. Only open a namespace if the existing one is
|
|
// different (closing/opening only what is necessary).
|
|
//
|
|
// The file must start and end with an empty (or null) namespace so that
|
|
// namespaces are properly opened and closed.
|
|
void SetNameSpace(const Namespace* ns) {
|
|
if (cur_name_space_ == ns) {
|
|
return;
|
|
}
|
|
|
|
// Compute the size of the longest common namespace prefix.
|
|
// If cur_name_space is A::B::C::D and ns is A::B::E::F::G,
|
|
// the common prefix is A::B:: and we have old_size = 4, new_size = 5
|
|
// and common_prefix_size = 2
|
|
size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0;
|
|
size_t new_size = ns ? ns->components.size() : 0;
|
|
|
|
size_t common_prefix_size = 0;
|
|
while (common_prefix_size < old_size && common_prefix_size < new_size &&
|
|
ns->components[common_prefix_size] ==
|
|
cur_name_space_->components[common_prefix_size]) {
|
|
common_prefix_size++;
|
|
}
|
|
|
|
// Close cur_name_space in reverse order to reach the common prefix.
|
|
// In the previous example, D then C are closed.
|
|
for (size_t j = old_size; j > common_prefix_size; --j) {
|
|
code_ += "} // namespace " + cur_name_space_->components[j - 1];
|
|
}
|
|
if (old_size != common_prefix_size) {
|
|
code_ += "";
|
|
}
|
|
|
|
// open namespace parts to reach the ns namespace
|
|
// in the previous example, E, then F, then G are opened
|
|
for (auto j = common_prefix_size; j != new_size; ++j) {
|
|
code_ += "namespace " + ns->components[j] + " {";
|
|
}
|
|
if (new_size != common_prefix_size) {
|
|
code_ += "";
|
|
}
|
|
|
|
cur_name_space_ = ns;
|
|
}
|
|
};
|
|
|
|
} // namespace cpp
|
|
|
|
static bool GenerateCPP(const Parser& parser, const std::string& path,
|
|
const std::string& file_name) {
|
|
cpp::IDLOptionsCpp opts(parser.opts);
|
|
// The '--cpp_std' argument could be extended (like ASAN):
|
|
// Example: "flatc --cpp_std c++17:option1:option2".
|
|
std::string cpp_std = !opts.cpp_std.empty() ? opts.cpp_std : "C++11";
|
|
std::transform(cpp_std.begin(), cpp_std.end(), cpp_std.begin(), CharToUpper);
|
|
if (cpp_std == "C++0X") {
|
|
opts.g_cpp_std = cpp::CPP_STD_X0;
|
|
opts.g_only_fixed_enums = false;
|
|
} else if (cpp_std == "C++11") {
|
|
// Use the standard C++11 code generator.
|
|
opts.g_cpp_std = cpp::CPP_STD_11;
|
|
opts.g_only_fixed_enums = true;
|
|
} else if (cpp_std == "C++17") {
|
|
opts.g_cpp_std = cpp::CPP_STD_17;
|
|
// With c++17 generate strong enums only.
|
|
opts.scoped_enums = true;
|
|
// By default, prefixed_enums==true, reset it.
|
|
opts.prefixed_enums = false;
|
|
} else {
|
|
LogCompilerError("Unknown value of the '--cpp-std' switch: " +
|
|
opts.cpp_std);
|
|
return false;
|
|
}
|
|
// The opts.scoped_enums has priority.
|
|
opts.g_only_fixed_enums |= opts.scoped_enums;
|
|
|
|
if (opts.cpp_static_reflection && opts.g_cpp_std < cpp::CPP_STD_17) {
|
|
LogCompilerError(
|
|
"--cpp-static-reflection requires using --cpp-std at \"C++17\" or "
|
|
"higher.");
|
|
return false;
|
|
}
|
|
|
|
cpp::CppGenerator generator(parser, path, file_name, opts);
|
|
return generator.generate();
|
|
}
|
|
|
|
static std::string CPPMakeRule(const Parser& parser, const std::string& path,
|
|
const std::string& file_name) {
|
|
const auto filebase = StripPath(StripExtension(file_name));
|
|
cpp::CppGenerator geneartor(parser, path, file_name, parser.opts);
|
|
const auto included_files = parser.GetIncludedFilesRecursive(file_name);
|
|
std::string make_rule =
|
|
geneartor.GeneratedFileName(path, filebase, parser.opts) + ": ";
|
|
for (const std::string& included_file : included_files) {
|
|
make_rule += " " + included_file;
|
|
}
|
|
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*, int64_t, const CodeGenOptions&) override {
|
|
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;
|
|
}
|
|
|
|
Status GenerateRootFile(const Parser& parser,
|
|
const std::string& path) override {
|
|
(void)parser;
|
|
(void)path;
|
|
return Status::NOT_IMPLEMENTED;
|
|
}
|
|
|
|
bool IsSchemaOnly() const override { return true; }
|
|
|
|
bool SupportsBfbsGeneration() const override { return false; }
|
|
|
|
bool SupportsRootFileGeneration() 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
|