From a9194c4c686ace7455b1ae5b146544d788feaafd Mon Sep 17 00:00:00 2001 From: lakedaemon Date: Thu, 2 Jun 2016 21:56:27 +0200 Subject: [PATCH 1/4] cpp generator : pulling all methods inside the class --- src/idl_gen_cpp.cpp | 1406 ++++++++++++++++---------------- tests/monster_test_generated.h | 4 +- 2 files changed, 698 insertions(+), 712 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 7efa1334e..875345933 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -22,706 +22,6 @@ #include "flatbuffers/code_generators.h" namespace flatbuffers { -namespace cpp { - -// This tracks the current namespace so we can insert namespace declarations. -// TODO(wvo): this needs to be moved into a code generator context object. -static const Namespace *code_generator_cur_name_space = nullptr; - -// Ensure that a type is prefixed with its namespace whenever it is used -// outside of its namespace. -static std::string WrapInNameSpace(const Namespace *ns, - const std::string &name) { - if (code_generator_cur_name_space != ns) { - std::string qualified_name; - for (auto it = ns->components.begin(); - it != ns->components.end(); ++it) { - qualified_name += *it + "::"; - } - return qualified_name + name; - } else { - return name; - } -} - -static std::string WrapInNameSpace(const Definition &def) { - return WrapInNameSpace(def.defined_namespace, def.name); -} - -// 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; -} - - -// Return a C++ type from the table in idl.h -static std::string GenTypeBasic(const Type &type, bool user_facing_type) { - static const char *ctypename[] = { - #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ - #CTYPE, - FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) - #undef FLATBUFFERS_TD - }; - if (user_facing_type) { - if (type.enum_def) return WrapInNameSpace(*type.enum_def); - if (type.base_type == BASE_TYPE_BOOL) return "bool"; - } - return ctypename[type.base_type]; -} - -static std::string GenTypeWire(const Parser &parser, const Type &type, - const char *postfix, bool user_facing_type); - -// Return a C++ pointer type, specialized to the actual struct/table types, -// and vector element types. -static std::string GenTypePointer(const Parser &parser, const Type &type) { - switch (type.base_type) { - case BASE_TYPE_STRING: - return "flatbuffers::String"; - case BASE_TYPE_VECTOR: - return "flatbuffers::Vector<" + - GenTypeWire(parser, type.VectorType(), "", false) + ">"; - 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. -static std::string GenTypeWire(const Parser &parser, const Type &type, - const char *postfix, bool user_facing_type) { - return IsScalar(type.base_type) - ? GenTypeBasic(type, user_facing_type) + postfix - : IsStruct(type) - ? "const " + GenTypePointer(parser, type) + " *" - : "flatbuffers::Offset<" + GenTypePointer(parser, type) + ">" + postfix; -} - -// Return a C++ type for any type (scalar/pointer) that reflects its -// serialized size. -static std::string GenTypeSize(const Parser &parser, const Type &type) { - return IsScalar(type.base_type) - ? GenTypeBasic(type, false) - : IsStruct(type) - ? GenTypePointer(parser, type) - : "flatbuffers::uoffset_t"; -} - -// Return a C++ type for any type (scalar/pointer) specifically for -// using a flatbuffer. -static std::string GenTypeGet(const Parser &parser, const Type &type, - const char *afterbasic, const char *beforeptr, - const char *afterptr, bool user_facing_type) { - return IsScalar(type.base_type) - ? GenTypeBasic(type, user_facing_type) + afterbasic - : beforeptr + GenTypePointer(parser, type) + afterptr; -} - -static std::string GenEnumDecl(const EnumDef &enum_def, - const IDLOptions &opts) { - return (opts.scoped_enums ? "enum class " : "enum ") + enum_def.name; -} - -static std::string GenEnumVal(const EnumDef &enum_def, - const std::string &enum_val, - const IDLOptions &opts) { - return opts.prefixed_enums ? enum_def.name + "_" + enum_val : enum_val; -} - -static std::string GetEnumVal(const EnumDef &enum_def, const EnumVal &enum_val, - const IDLOptions &opts) { - if (opts.scoped_enums) { - return enum_def.name + "::" + enum_val.name; - } else if (opts.prefixed_enums) { - return enum_def.name + "_" + enum_val.name; - } else { - return enum_val.name; - } -} - -std::string EnumSignature(EnumDef &enum_def) { - return "inline bool Verify" + enum_def.name + - "(flatbuffers::Verifier &verifier, " + - "const void *union_obj, " + enum_def.name + " type)"; -} - -// Generate an enum declaration and an enum string lookup table. -static void GenEnum(const Parser &parser, EnumDef &enum_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - GenComment(enum_def.doc_comment, code_ptr, nullptr); - code += GenEnumDecl(enum_def, parser.opts); - if (parser.opts.scoped_enums) - code += " : " + GenTypeBasic(enum_def.underlying_type, false); - code += " {\n"; - int64_t anyv = 0; - EnumVal *minv = nullptr, *maxv = nullptr; - for (auto it = enum_def.vals.vec.begin(); - it != enum_def.vals.vec.end(); - ++it) { - auto &ev = **it; - GenComment(ev.doc_comment, code_ptr, nullptr, " "); - code += " " + GenEnumVal(enum_def, ev.name, parser.opts) + " = "; - code += NumToString(ev.value) + ",\n"; - minv = !minv || minv->value > ev.value ? &ev : minv; - maxv = !maxv || maxv->value < ev.value ? &ev : maxv; - anyv |= ev.value; - } - if (parser.opts.scoped_enums || parser.opts.prefixed_enums) { - assert(minv && maxv); - if (enum_def.attributes.Lookup("bit_flags")) { - if (minv->value != 0) // If the user didn't defined NONE value - code += " " + GenEnumVal(enum_def, "NONE", parser.opts) + " = 0,\n"; - if (maxv->value != anyv) // If the user didn't defined ANY value - code += " " + GenEnumVal(enum_def, "ANY", parser.opts) + " = " + NumToString(anyv) + "\n"; - } else { // MIN & MAX are useless for bit_flags - code += " " + GenEnumVal(enum_def, "MIN", parser.opts) + " = "; - code += GenEnumVal(enum_def, minv->name, parser.opts) + ",\n"; - code += " " + GenEnumVal(enum_def, "MAX", parser.opts) + " = "; - code += GenEnumVal(enum_def, maxv->name, parser.opts) + "\n"; - } - } - code += "};\n"; - if (parser.opts.scoped_enums && enum_def.attributes.Lookup("bit_flags")) - code += "DEFINE_BITMASK_OPERATORS(" + enum_def.name + ", " + GenTypeBasic(enum_def.underlying_type, false) + ")\n"; - code += "\n"; - - // Generate a generate string table for enum values. - // Problem is, if values are very sparse that could generate really big - // tables. Ideally in that case we generate a map lookup instead, but for - // the moment we simply don't output a table at all. - auto range = enum_def.vals.vec.back()->value - - enum_def.vals.vec.front()->value + 1; - // Average distance between values above which we consider a table - // "too sparse". Change at will. - static const int kMaxSparseness = 5; - if (range / static_cast(enum_def.vals.vec.size()) < kMaxSparseness) { - code += "inline const char **EnumNames" + enum_def.name + "() {\n"; - code += " static const char *names[] = { "; - auto val = enum_def.vals.vec.front()->value; - for (auto it = enum_def.vals.vec.begin(); - it != enum_def.vals.vec.end(); - ++it) { - while (val++ != (*it)->value) code += "\"\", "; - code += "\"" + (*it)->name + "\", "; - } - code += "nullptr };\n return names;\n}\n\n"; - code += "inline const char *EnumName" + enum_def.name; - code += "(" + enum_def.name + " e) { return EnumNames" + enum_def.name; - code += "()[static_cast(e)"; - if (enum_def.vals.vec.front()->value) { - code += " - static_cast("; - code += GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser.opts) +")"; - } - code += "]; }\n\n"; - } - - if (enum_def.is_union) { - code += EnumSignature(enum_def) + ";\n\n"; - } -} - -static void GenEnumPost(const Parser &parser, EnumDef &enum_def, - std::string *code_ptr_post) { - // 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. - std::string &code_post = *code_ptr_post; - code_post += EnumSignature(enum_def) + " {\n switch (type) {\n"; - for (auto it = enum_def.vals.vec.begin(); - it != enum_def.vals.vec.end(); - ++it) { - auto &ev = **it; - code_post += " case " + GetEnumVal(enum_def, ev, parser.opts); - if (!ev.value) { - code_post += ": return true;\n"; // "NONE" enum value. - } else { - code_post += ": return verifier.VerifyTable(reinterpret_cast(" + val + ")"; - } else { - return val; - } -} - -std::string GenFieldOffsetName(const FieldDef &field) { - std::string uname = field.name; - std::transform(uname.begin(), uname.end(), uname.begin(), ::toupper); - return "VT_" + uname; -} - -static void GenFullyQualifiedNameGetter(const Parser &parser, const std::string& name, std::string &code) { - if (parser.opts.generate_name_strings) { - code += " static FLATBUFFERS_CONSTEXPR const char *GetFullyQualifiedName() {\n"; - code += " return \"" + parser.namespaces_.back()->GetFullyQualifiedName(name) + "\";\n"; - code += " }\n"; - } -} - -std::string GenDefaultConstant(const FieldDef &field) { - return field.value.type.base_type == BASE_TYPE_FLOAT - ? field.value.constant + "f" - : field.value.constant; -} - -// Generate an accessor struct, builder structs & function for a table. -static void GenTable(const Parser &parser, StructDef &struct_def, - std::string *code_ptr) { - std::string &code = *code_ptr; - // Generate an accessor struct, with methods of the form: - // type name() const { return GetField(offset, defaultval); } - GenComment(struct_def.doc_comment, code_ptr, nullptr); - code += "struct " + struct_def.name; - code += " FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table"; - code += " {\n"; - // Generate GetFullyQualifiedName - GenFullyQualifiedNameGetter(parser, struct_def.name, code); - // Generate field id constants. - if (struct_def.fields.vec.size() > 0) { - code += " enum {\n"; - bool is_first_field = true; // track the first field that's not deprecated - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (!field.deprecated) { // Deprecated fields won't be accessible. - if (!is_first_field) { - // Add trailing comma and newline to previous element. Don't add trailing comma to - // last element since older versions of gcc complain about this. - code += ",\n"; - } else { - is_first_field = false; - } - code += " " + GenFieldOffsetName(field) + " = "; - code += NumToString(field.value.offset); - } - } - code += "\n };\n"; - } - // Generate the accessors. - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (!field.deprecated) { // Deprecated fields won't be accessible. - auto is_scalar = IsScalar(field.value.type.base_type); - GenComment(field.doc_comment, code_ptr, nullptr, " "); - code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " *", - true); - code += field.name + "() const { return "; - // Call a different accessor for pointers, that indirects. - auto accessor = is_scalar - ? "GetField<" - : (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<"); - auto offsetstr = GenFieldOffsetName(field); - auto call = - accessor + - GenTypeGet(parser, field.value.type, "", "const ", " *", false) + - ">(" + offsetstr; - // Default value as second arg for non-pointer types. - if (IsScalar(field.value.type.base_type)) - call += ", " + GenDefaultConstant(field); - call += ")"; - code += GenUnderlyingCast(field, true, call); - code += "; }\n"; - if (parser.opts.mutable_buffer) { - if (is_scalar) { - code += " bool mutate_" + field.name + "("; - code += GenTypeBasic(field.value.type, true); - code += " _" + field.name + ") { return SetField(" + offsetstr + ", "; - code += GenUnderlyingCast(field, false, "_" + field.name); - code += "); }\n"; - } else { - auto type = GenTypeGet(parser, field.value.type, " ", "", " *", true); - code += " " + type + "mutable_" + field.name + "() { return "; - code += GenUnderlyingCast(field, true, - accessor + type + ">(" + offsetstr + ")"); - code += "; }\n"; - } - } - auto nested = field.attributes.Lookup("nested_flatbuffer"); - if (nested) { - std::string qualified_name = - parser.namespaces_.back()->GetFullyQualifiedName(nested->constant); - auto nested_root = parser.structs_.Lookup(qualified_name); - assert(nested_root); // Guaranteed to exist by parser. - (void)nested_root; - std::string cpp_qualified_name = TranslateNameSpace(qualified_name); - - code += " const " + cpp_qualified_name + " *" + field.name; - code += "_nested_root() const { return flatbuffers::GetRoot<"; - code += cpp_qualified_name + ">(" + field.name + "()->Data()); }\n"; - } - // Generate a comparison function for this field if it is a key. - if (field.key) { - code += " bool KeyCompareLessThan(const " + struct_def.name; - code += " *o) const { return "; - if (field.value.type.base_type == BASE_TYPE_STRING) code += "*"; - code += field.name + "() < "; - if (field.value.type.base_type == BASE_TYPE_STRING) code += "*"; - code += "o->" + field.name + "(); }\n"; - code += " int KeyCompareWithValue("; - if (field.value.type.base_type == BASE_TYPE_STRING) { - code += "const char *val) const { return strcmp(" + field.name; - code += "()->c_str(), val); }\n"; - } else { - if (parser.opts.scoped_enums && - field.value.type.enum_def && - IsScalar(field.value.type.base_type)) { - code += GenTypeGet(parser, field.value.type, " ", "const ", " *", - true); - } else { - code += GenTypeBasic(field.value.type, false); - } - code += " val) const { return " + field.name + "() < val ? -1 : "; - code += field.name + "() > val; }\n"; - } - } - } - } - // Generate a verifier function that can check a buffer from an untrusted - // source will never cause reads outside the buffer. - code += " bool Verify(flatbuffers::Verifier &verifier) const {\n"; - code += " return VerifyTableStart(verifier)"; - std::string prefix = " &&\n "; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (!field.deprecated) { - code += prefix + "VerifyField"; - if (field.required) code += "Required"; - code += "<" + GenTypeSize(parser, field.value.type); - code += ">(verifier, " + GenFieldOffsetName(field) + ")"; - switch (field.value.type.base_type) { - case BASE_TYPE_UNION: - code += prefix + "Verify" + field.value.type.enum_def->name; - code += "(verifier, " + field.name + "(), " + field.name + "_type())"; - break; - case BASE_TYPE_STRUCT: - if (!field.value.type.struct_def->fixed) { - code += prefix + "verifier.VerifyTable(" + field.name; - code += "())"; - } - break; - case BASE_TYPE_STRING: - code += prefix + "verifier.Verify(" + field.name + "())"; - break; - case BASE_TYPE_VECTOR: - code += prefix + "verifier.Verify(" + field.name + "())"; - switch (field.value.type.element) { - case BASE_TYPE_STRING: { - code += prefix + "verifier.VerifyVectorOfStrings(" + field.name; - code += "())"; - break; - } - case BASE_TYPE_STRUCT: { - if (!field.value.type.struct_def->fixed) { - code += prefix + "verifier.VerifyVectorOfTables(" + field.name; - code += "())"; - } - break; - } - default: - break; - } - break; - default: - break; - } - } - } - code += prefix + "verifier.EndTable()"; - code += ";\n }\n"; - code += "};\n\n"; - - // Generate a builder struct, with methods of the form: - // void add_name(type name) { fbb_.AddElement(offset, name, default); } - code += "struct " + struct_def.name; - code += "Builder {\n flatbuffers::FlatBufferBuilder &fbb_;\n"; - code += " flatbuffers::uoffset_t start_;\n"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (!field.deprecated) { - code += " void add_" + field.name + "("; - code += GenTypeWire(parser, field.value.type, " ", true) + field.name; - code += ") { fbb_.Add"; - if (IsScalar(field.value.type.base_type)) { - code += "Element<" + GenTypeWire(parser, field.value.type, "", false); - code += ">"; - } else if (IsStruct(field.value.type)) { - code += "Struct"; - } else { - code += "Offset"; - } - code += "(" + struct_def.name + "::" + GenFieldOffsetName(field) + ", "; - code += GenUnderlyingCast(field, false, field.name); - if (IsScalar(field.value.type.base_type)) - code += ", " + GenDefaultConstant(field); - code += "); }\n"; - } - } - code += " " + struct_def.name; - code += "Builder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) "; - code += "{ start_ = fbb_.StartTable(); }\n"; - code += " " + struct_def.name + "Builder &operator=(const "; - code += struct_def.name + "Builder &);\n"; - code += " flatbuffers::Offset<" + struct_def.name; - code += "> Finish() {\n auto o = flatbuffers::Offset<" + struct_def.name; - code += ">(fbb_.EndTable(start_, "; - code += NumToString(struct_def.fields.vec.size()) + "));\n"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (!field.deprecated && field.required) { - code += " fbb_.Required(o, "; - code += struct_def.name + "::" + GenFieldOffsetName(field); - code += "); // " + field.name + "\n"; - } - } - code += " return o;\n }\n};\n\n"; - - // Generate a convenient CreateX function that uses the above builder - // to create a table in one go. - code += "inline flatbuffers::Offset<" + struct_def.name + "> Create"; - code += struct_def.name; - code += "(flatbuffers::FlatBufferBuilder &_fbb"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (!field.deprecated) { - code += ",\n " + GenTypeWire(parser, field.value.type, " ", true); - code += field.name + " = "; - if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) { - auto ev = field.value.type.enum_def->ReverseLookup( - static_cast(StringToInt(field.value.constant.c_str())), false); - if (ev) { - code += WrapInNameSpace(field.value.type.enum_def->defined_namespace, - GetEnumVal(*field.value.type.enum_def, *ev, - parser.opts)); - } else { - code += GenUnderlyingCast(field, true, field.value.constant); - } - } else if (field.value.type.base_type == BASE_TYPE_BOOL) { - code += field.value.constant == "0" ? "false" : "true"; - } else { - code += GenDefaultConstant(field); - } - } - } - code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n"; - 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) { - auto &field = **it; - if (!field.deprecated && - (!struct_def.sortbysize || - size == SizeOf(field.value.type.base_type))) { - code += " builder_.add_" + field.name + "(" + field.name + ");\n"; - } - } - } - code += " return builder_.Finish();\n}\n\n"; -} - -static void GenPadding(const FieldDef &field, std::string &code, - int &padding_id, - const std::function &f) { - if (field.padding) { - for (int i = 0; i < 4; i++) - if (static_cast(field.padding) & (1 << i)) - f((1 << i) * 8, code, padding_id); - assert(!(field.padding & ~0xF)); - } -} - -static void PaddingDefinition(int bits, std::string &code, int &padding_id) { - code += " int" + NumToString(bits) + - "_t __padding" + NumToString(padding_id++) + ";\n"; -} - -static void PaddingDeclaration(int bits, std::string &code, int &padding_id) { - (void)bits; - code += " (void)__padding" + NumToString(padding_id++) + ";"; -} - -static void PaddingInitializer(int bits, std::string &code, int &padding_id) { - (void)bits; - code += ", __padding" + NumToString(padding_id++) + "(0)"; -} - -// Generate an accessor struct with constructor for a flatbuffers struct. -static void GenStruct(const Parser &parser, StructDef &struct_def, - std::string *code_ptr) { - if (struct_def.generated) return; - std::string &code = *code_ptr; - - // 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_ptr, nullptr); - code += "MANUALLY_ALIGNED_STRUCT(" + NumToString(struct_def.minalign) + ") "; - code += struct_def.name + " FLATBUFFERS_FINAL_CLASS {\n private:\n"; - int padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - code += " " + GenTypeGet(parser, field.value.type, " ", "", " ", false); - code += field.name + "_;\n"; - GenPadding(field, code, padding_id, PaddingDefinition); - } - - // Generate GetFullyQualifiedName - code += "\n public:\n"; - GenFullyQualifiedNameGetter(parser, struct_def.name, code); - - // Generate a constructor that takes all fields as arguments. - code += " " + struct_def.name + "("; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (it != struct_def.fields.vec.begin()) code += ", "; - code += GenTypeGet(parser, field.value.type, " ", "const ", " &", true); - code += "_" + field.name; - } - code += ")\n : "; - padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - if (it != struct_def.fields.vec.begin()) code += ", "; - code += field.name + "_("; - if (IsScalar(field.value.type.base_type)) { - code += "flatbuffers::EndianScalar("; - code += GenUnderlyingCast(field, false, "_" + field.name); - code += "))"; - } else { - code += "_" + field.name + ")"; - } - GenPadding(field, code, padding_id, PaddingInitializer); - } - - code += " {"; - padding_id = 0; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - GenPadding(field, code, padding_id, PaddingDeclaration); - } - code += " }\n\n"; - - // Generate accessor methods of the form: - // type name() const { return flatbuffers::EndianScalar(name_); } - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); - ++it) { - auto &field = **it; - GenComment(field.doc_comment, code_ptr, nullptr, " "); - auto is_scalar = IsScalar(field.value.type.base_type); - code += " " + GenTypeGet(parser, field.value.type, " ", "const ", " &", - true); - code += field.name + "() const { return "; - code += GenUnderlyingCast(field, true, - is_scalar - ? "flatbuffers::EndianScalar(" + field.name + "_)" - : field.name + "_"); - code += "; }\n"; - if (parser.opts.mutable_buffer) { - if (is_scalar) { - code += " void mutate_" + field.name + "("; - code += GenTypeBasic(field.value.type, true); - code += " _" + field.name + ") { flatbuffers::WriteScalar(&"; - code += field.name + "_, "; - code += GenUnderlyingCast(field, false, "_" + field.name); - code += "); }\n"; - } else { - code += " "; - code += GenTypeGet(parser, field.value.type, "", "", " &", true); - code += "mutable_" + field.name + "() { return " + field.name; - code += "_; }\n"; - } - } - } - code += "};\nSTRUCT_END(" + struct_def.name + ", "; - code += NumToString(struct_def.bytesize) + ");\n\n"; -} - -void GenerateNestedNameSpaces(const Namespace *ns, std::string *code_ptr) { - for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { - *code_ptr += "namespace " + *it + " {\n"; - } -} - -void CloseNestedNameSpaces(const Namespace *ns, std::string *code_ptr) { - for (auto it = ns->components.rbegin(); it != ns->components.rend(); ++it) { - *code_ptr += "} // namespace " + *it + "\n"; - } -} - -void CheckNameSpace(const Definition &def, std::string *code_ptr) { - // Set up the correct namespace. Only open a namespace if - // the existing one is different. - // TODO: this could be done more intelligently, by sorting to - // namespace path and only opening/closing what is necessary, but that's - // quite a bit more complexity. - if (code_generator_cur_name_space != def.defined_namespace) { - if (code_generator_cur_name_space) { - CloseNestedNameSpaces(code_generator_cur_name_space, code_ptr); - if (code_generator_cur_name_space->components.size()) *code_ptr += "\n"; - } - GenerateNestedNameSpaces(def.defined_namespace, code_ptr); - code_generator_cur_name_space = def.defined_namespace; - if (code_generator_cur_name_space->components.size()) *code_ptr += "\n"; - } -} - -} // namespace cpp struct IsAlnum { bool operator()(char c) { @@ -790,8 +90,8 @@ class CppGenerator : public BaseGenerator { // Generate forward declarations for all structs/tables, since they may // have circular references. - for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); - ++it) { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (!struct_def.generated) { CheckNameSpace(struct_def, &code); @@ -805,25 +105,25 @@ class CppGenerator : public BaseGenerator { auto &enum_def = **it; if (!enum_def.generated) { CheckNameSpace(**it, &code); - GenEnum(parser_, **it, &code); + GenEnum(**it, &code); } } // Generate code for all structs, then all tables. - for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); - ++it) { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (struct_def.fixed && !struct_def.generated) { CheckNameSpace(struct_def, &code); - GenStruct(parser_, struct_def, &code); + GenStruct(struct_def, &code); } } - for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); - ++it) { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (!struct_def.fixed && !struct_def.generated) { CheckNameSpace(struct_def, &code); - GenTable(parser_, struct_def, &code); + GenTable(struct_def, &code); } } @@ -833,7 +133,7 @@ class CppGenerator : public BaseGenerator { auto &enum_def = **it; if (enum_def.is_union && !enum_def.generated) { CheckNameSpace(enum_def, &code); - GenEnumPost(parser_, enum_def, &code); + GenEnumPost(enum_def, &code); } } @@ -905,6 +205,692 @@ class CppGenerator : public BaseGenerator { return SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false); } + + private: + // This tracks the current namespace so we can insert namespace declarations. + const Namespace *code_generator_cur_name_space = nullptr; + + // Ensure that a type is prefixed with its namespace whenever it is used + // outside of its namespace. + std::string WrapInNameSpace(const Namespace *ns, const std::string &name) { + if (code_generator_cur_name_space != ns) { + std::string qualified_name; + for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { + qualified_name += *it + "::"; + } + return qualified_name + name; + } else { + return name; + } + } + + std::string WrapInNameSpace(const Definition &def) { + return WrapInNameSpace(def.defined_namespace, def.name); + } + + // 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; + } + + // Return a C++ type from the table in idl.h + std::string GenTypeBasic(const Type &type, bool user_facing_type) { + static const char *ctypename[] = { +#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) #CTYPE, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) +#undef FLATBUFFERS_TD + }; + if (user_facing_type) { + if (type.enum_def) return WrapInNameSpace(*type.enum_def); + if (type.base_type == BASE_TYPE_BOOL) return "bool"; + } + return ctypename[type.base_type]; + } + + // Return a C++ pointer type, specialized to the actual struct/table types, + // and vector element types. + std::string GenTypePointer(const Type &type) { + switch (type.base_type) { + case BASE_TYPE_STRING: + return "flatbuffers::String"; + case BASE_TYPE_VECTOR: + return "flatbuffers::Vector<" + + GenTypeWire(type.VectorType(), "", false) + ">"; + 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) { + return IsScalar(type.base_type) + ? GenTypeBasic(type, user_facing_type) + postfix + : IsStruct(type) + ? "const " + GenTypePointer(type) + " *" + : "flatbuffers::Offset<" + GenTypePointer(type) + + ">" + postfix; + } + + // Return a C++ type for any type (scalar/pointer) that reflects its + // serialized size. + std::string GenTypeSize(const Type &type) { + return IsScalar(type.base_type) + ? GenTypeBasic(type, false) + : IsStruct(type) ? GenTypePointer(type) + : "flatbuffers::uoffset_t"; + } + + // 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) { + return IsScalar(type.base_type) + ? GenTypeBasic(type, user_facing_type) + afterbasic + : beforeptr + GenTypePointer(type) + afterptr; + } + + static std::string GenEnumDecl(const EnumDef &enum_def, + const IDLOptions &opts) { + return (opts.scoped_enums ? "enum class " : "enum ") + enum_def.name; + } + + static std::string GenEnumVal(const EnumDef &enum_def, + const std::string &enum_val, + const IDLOptions &opts) { + return opts.prefixed_enums ? enum_def.name + "_" + enum_val : enum_val; + } + + static std::string GetEnumVal(const EnumDef &enum_def, + const EnumVal &enum_val, + const IDLOptions &opts) { + if (opts.scoped_enums) { + return enum_def.name + "::" + enum_val.name; + } else if (opts.prefixed_enums) { + return enum_def.name + "_" + enum_val.name; + } else { + return enum_val.name; + } + } + + std::string EnumSignature(EnumDef &enum_def) { + return "inline bool Verify" + enum_def.name + + "(flatbuffers::Verifier &verifier, " + "const void *union_obj, " + + enum_def.name + " type)"; + } + + // Generate an enum declaration and an enum string lookup table. + void GenEnum(EnumDef &enum_def, std::string *code_ptr) { + std::string &code = *code_ptr; + GenComment(enum_def.doc_comment, code_ptr, nullptr); + code += GenEnumDecl(enum_def, parser_.opts); + if (parser_.opts.scoped_enums) + code += " : " + GenTypeBasic(enum_def.underlying_type, false); + code += " {\n"; + int64_t anyv = 0; + EnumVal *minv = nullptr, *maxv = nullptr; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + GenComment(ev.doc_comment, code_ptr, nullptr, " "); + code += " " + GenEnumVal(enum_def, ev.name, parser_.opts) + " = "; + code += NumToString(ev.value) + ",\n"; + minv = !minv || minv->value > ev.value ? &ev : minv; + maxv = !maxv || maxv->value < ev.value ? &ev : maxv; + anyv |= ev.value; + } + if (parser_.opts.scoped_enums || parser_.opts.prefixed_enums) { + assert(minv && maxv); + if (enum_def.attributes.Lookup("bit_flags")) { + if (minv->value != 0) // If the user didn't defined NONE value + code += " " + GenEnumVal(enum_def, "NONE", parser_.opts) + " = 0,\n"; + if (maxv->value != anyv) // If the user didn't defined ANY value + code += " " + GenEnumVal(enum_def, "ANY", parser_.opts) + " = " + + NumToString(anyv) + "\n"; + } else { // MIN & MAX are useless for bit_flags + code += " " + GenEnumVal(enum_def, "MIN", parser_.opts) + " = "; + code += GenEnumVal(enum_def, minv->name, parser_.opts) + ",\n"; + code += " " + GenEnumVal(enum_def, "MAX", parser_.opts) + " = "; + code += GenEnumVal(enum_def, maxv->name, parser_.opts) + "\n"; + } + } + code += "};\n"; + if (parser_.opts.scoped_enums && enum_def.attributes.Lookup("bit_flags")) + code += "DEFINE_BITMASK_OPERATORS(" + enum_def.name + ", " + + GenTypeBasic(enum_def.underlying_type, false) + ")\n"; + code += "\n"; + + // Generate a generate string table for enum values. + // Problem is, if values are very sparse that could generate really big + // tables. Ideally in that case we generate a map lookup instead, but for + // the moment we simply don't output a table at all. + auto range = + enum_def.vals.vec.back()->value - enum_def.vals.vec.front()->value + 1; + // Average distance between values above which we consider a table + // "too sparse". Change at will. + static const int kMaxSparseness = 5; + if (range / static_cast(enum_def.vals.vec.size()) < + kMaxSparseness) { + code += "inline const char **EnumNames" + enum_def.name + "() {\n"; + code += " static const char *names[] = { "; + auto val = enum_def.vals.vec.front()->value; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + while (val++ != (*it)->value) code += "\"\", "; + code += "\"" + (*it)->name + "\", "; + } + code += "nullptr };\n return names;\n}\n\n"; + code += "inline const char *EnumName" + enum_def.name; + code += "(" + enum_def.name + " e) { return EnumNames" + enum_def.name; + code += "()[static_cast(e)"; + if (enum_def.vals.vec.front()->value) { + code += " - static_cast("; + code += + GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser_.opts) + ")"; + } + code += "]; }\n\n"; + } + + if (enum_def.is_union) { + code += EnumSignature(enum_def) + ";\n\n"; + } + } + + void GenEnumPost(EnumDef &enum_def, std::string *code_ptr_post) { + // 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. + std::string &code_post = *code_ptr_post; + code_post += EnumSignature(enum_def) + " {\n switch (type) {\n"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + code_post += " case " + GetEnumVal(enum_def, ev, parser_.opts); + if (!ev.value) { + code_post += ": return true;\n"; // "NONE" enum value. + } else { + code_post += ": return verifier.VerifyTable(reinterpret_cast(" + + val + ")"; + } else { + return val; + } + } + + std::string GenFieldOffsetName(const FieldDef &field) { + std::string uname = field.name; + std::transform(uname.begin(), uname.end(), uname.begin(), ::toupper); + return "VT_" + uname; + } + + void GenFullyQualifiedNameGetter(const std::string &name, + std::string &code) { + if (parser_.opts.generate_name_strings) { + code += + " static FLATBUFFERS_CONSTEXPR const char *GetFullyQualifiedName() " + "{\n"; + code += " return \"" + + parser_.namespaces_.back()->GetFullyQualifiedName(name) + "\";\n"; + code += " }\n"; + } + } + + std::string GenDefaultConstant(const FieldDef &field) { + return field.value.type.base_type == BASE_TYPE_FLOAT + ? field.value.constant + "f" + : field.value.constant; + } + + // Generate an accessor struct, builder structs & function for a table. + void GenTable(StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + // Generate an accessor struct, with methods of the form: + // type name() const { return GetField(offset, defaultval); } + GenComment(struct_def.doc_comment, code_ptr, nullptr); + code += "struct " + struct_def.name; + code += " FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table"; + code += " {\n"; + // Generate GetFullyQualifiedName + GenFullyQualifiedNameGetter(struct_def.name, code); + // Generate field id constants. + if (struct_def.fields.vec.size() > 0) { + code += " enum {\n"; + bool is_first_field = + true; // track the first field that's not deprecated + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { // Deprecated fields won't be accessible. + if (!is_first_field) { + // Add trailing comma and newline to previous element. Don't add + // trailing comma to + // last element since older versions of gcc complain about this. + code += ",\n"; + } else { + is_first_field = false; + } + code += " " + GenFieldOffsetName(field) + " = "; + code += NumToString(field.value.offset); + } + } + code += "\n };\n"; + } + // Generate the accessors. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { // Deprecated fields won't be accessible. + auto is_scalar = IsScalar(field.value.type.base_type); + GenComment(field.doc_comment, code_ptr, nullptr, " "); + code += " " + + GenTypeGet(field.value.type, " ", "const ", " *", true); + code += field.name + "() const { return "; + // Call a different accessor for pointers, that indirects. + auto accessor = + is_scalar + ? "GetField<" + : (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<"); + auto offsetstr = GenFieldOffsetName(field); + auto call = accessor + GenTypeGet(field.value.type, "", + "const ", " *", false) + + ">(" + offsetstr; + // Default value as second arg for non-pointer types. + if (IsScalar(field.value.type.base_type)) + call += ", " + GenDefaultConstant(field); + call += ")"; + code += GenUnderlyingCast(field, true, call); + code += "; }\n"; + if (parser_.opts.mutable_buffer) { + if (is_scalar) { + code += " bool mutate_" + field.name + "("; + code += GenTypeBasic(field.value.type, true); + code += + " _" + field.name + ") { return SetField(" + offsetstr + ", "; + code += GenUnderlyingCast(field, false, "_" + field.name); + code += "); }\n"; + } else { + auto type = + GenTypeGet(field.value.type, " ", "", " *", true); + code += " " + type + "mutable_" + field.name + "() { return "; + code += GenUnderlyingCast(field, true, + accessor + type + ">(" + offsetstr + ")"); + code += "; }\n"; + } + } + auto nested = field.attributes.Lookup("nested_flatbuffer"); + if (nested) { + std::string qualified_name = + parser_.namespaces_.back()->GetFullyQualifiedName( + nested->constant); + auto nested_root = parser_.structs_.Lookup(qualified_name); + assert(nested_root); // Guaranteed to exist by parser. + (void)nested_root; + std::string cpp_qualified_name = TranslateNameSpace(qualified_name); + + code += " const " + cpp_qualified_name + " *" + field.name; + code += "_nested_root() const { return flatbuffers::GetRoot<"; + code += cpp_qualified_name + ">(" + field.name + "()->Data()); }\n"; + } + // Generate a comparison function for this field if it is a key. + if (field.key) { + code += " bool KeyCompareLessThan(const " + struct_def.name; + code += " *o) const { return "; + if (field.value.type.base_type == BASE_TYPE_STRING) code += "*"; + code += field.name + "() < "; + if (field.value.type.base_type == BASE_TYPE_STRING) code += "*"; + code += "o->" + field.name + "(); }\n"; + code += " int KeyCompareWithValue("; + if (field.value.type.base_type == BASE_TYPE_STRING) { + code += "const char *val) const { return strcmp(" + field.name; + code += "()->c_str(), val); }\n"; + } else { + if (parser_.opts.scoped_enums && field.value.type.enum_def && + IsScalar(field.value.type.base_type)) { + code += GenTypeGet(field.value.type, " ", "const ", " *", true); + } else { + code += GenTypeBasic(field.value.type, false); + } + code += " val) const { return " + field.name + "() < val ? -1 : "; + code += field.name + "() > val; }\n"; + } + } + } + } + // Generate a verifier function that can check a buffer from an untrusted + // source will never cause reads outside the buffer. + code += " bool Verify(flatbuffers::Verifier &verifier) const {\n"; + code += " return VerifyTableStart(verifier)"; + std::string prefix = " &&\n "; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + code += prefix + "VerifyField"; + if (field.required) code += "Required"; + code += "<" + GenTypeSize(field.value.type); + code += ">(verifier, " + GenFieldOffsetName(field) + ")"; + switch (field.value.type.base_type) { + case BASE_TYPE_UNION: + code += prefix + "Verify" + field.value.type.enum_def->name; + code += + "(verifier, " + field.name + "(), " + field.name + "_type())"; + break; + case BASE_TYPE_STRUCT: + if (!field.value.type.struct_def->fixed) { + code += prefix + "verifier.VerifyTable(" + field.name; + code += "())"; + } + break; + case BASE_TYPE_STRING: + code += prefix + "verifier.Verify(" + field.name + "())"; + break; + case BASE_TYPE_VECTOR: + code += prefix + "verifier.Verify(" + field.name + "())"; + switch (field.value.type.element) { + case BASE_TYPE_STRING: { + code += prefix + "verifier.VerifyVectorOfStrings(" + field.name; + code += "())"; + break; + } + case BASE_TYPE_STRUCT: { + if (!field.value.type.struct_def->fixed) { + code += + prefix + "verifier.VerifyVectorOfTables(" + field.name; + code += "())"; + } + break; + } + default: + break; + } + break; + default: + break; + } + } + } + code += prefix + "verifier.EndTable()"; + code += ";\n }\n"; + code += "};\n\n"; + + // Generate a builder struct, with methods of the form: + // void add_name(type name) { fbb_.AddElement(offset, name, default); + // } + code += "struct " + struct_def.name; + code += "Builder {\n flatbuffers::FlatBufferBuilder &fbb_;\n"; + code += " flatbuffers::uoffset_t start_;\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + code += " void add_" + field.name + "("; + code += GenTypeWire(field.value.type, " ", true) + field.name; + code += ") { fbb_.Add"; + if (IsScalar(field.value.type.base_type)) { + code += "Element<" + GenTypeWire(field.value.type, "", false); + code += ">"; + } else if (IsStruct(field.value.type)) { + code += "Struct"; + } else { + code += "Offset"; + } + code += "(" + struct_def.name + "::" + GenFieldOffsetName(field) + ", "; + code += GenUnderlyingCast(field, false, field.name); + if (IsScalar(field.value.type.base_type)) + code += ", " + GenDefaultConstant(field); + code += "); }\n"; + } + } + code += " " + struct_def.name; + code += "Builder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) "; + code += "{ start_ = fbb_.StartTable(); }\n"; + code += " " + struct_def.name + "Builder &operator=(const "; + code += struct_def.name + "Builder &);\n"; + code += " flatbuffers::Offset<" + struct_def.name; + code += "> Finish() {\n auto o = flatbuffers::Offset<" + struct_def.name; + code += ">(fbb_.EndTable(start_, "; + code += NumToString(struct_def.fields.vec.size()) + "));\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated && field.required) { + code += " fbb_.Required(o, "; + code += struct_def.name + "::" + GenFieldOffsetName(field); + code += "); // " + field.name + "\n"; + } + } + code += " return o;\n }\n};\n\n"; + + // Generate a convenient CreateX function that uses the above builder + // to create a table in one go. + code += "inline flatbuffers::Offset<" + struct_def.name + "> Create"; + code += struct_def.name; + code += "(flatbuffers::FlatBufferBuilder &_fbb"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + code += ",\n " + GenTypeWire(field.value.type, " ", true); + code += field.name + " = "; + if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) { + auto ev = field.value.type.enum_def->ReverseLookup( + static_cast(StringToInt(field.value.constant.c_str())), + false); + if (ev) { + code += WrapInNameSpace( + field.value.type.enum_def->defined_namespace, + GetEnumVal(*field.value.type.enum_def, *ev, parser_.opts)); + } else { + code += GenUnderlyingCast(field, true, field.value.constant); + } + } else if (field.value.type.base_type == BASE_TYPE_BOOL) { + code += field.value.constant == "0" ? "false" : "true"; + } else { + code += GenDefaultConstant(field); + } + } + } + code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n"; + 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) { + auto &field = **it; + if (!field.deprecated && (!struct_def.sortbysize || + size == SizeOf(field.value.type.base_type))) { + code += " builder_.add_" + field.name + "(" + field.name + ");\n"; + } + } + } + code += " return builder_.Finish();\n}\n\n"; + } + + static void GenPadding(const FieldDef &field, std::string &code, + int &padding_id, + const std::function &f) { + if (field.padding) { + for (int i = 0; i < 4; i++) + if (static_cast(field.padding) & (1 << i)) + f((1 << i) * 8, code, padding_id); + assert(!(field.padding & ~0xF)); + } + } + + static void PaddingDefinition(int bits, std::string &code, int &padding_id) { + code += " int" + NumToString(bits) + "_t __padding" + + NumToString(padding_id++) + ";\n"; + } + + static void PaddingDeclaration(int bits, std::string &code, int &padding_id) { + (void)bits; + code += " (void)__padding" + NumToString(padding_id++) + ";"; + } + + static void PaddingInitializer(int bits, std::string &code, int &padding_id) { + (void)bits; + code += ", __padding" + NumToString(padding_id++) + "(0)"; + } + + // Generate an accessor struct with constructor for a flatbuffers struct. + void GenStruct(StructDef &struct_def, std::string *code_ptr) { + if (struct_def.generated) return; + std::string &code = *code_ptr; + + // 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_ptr, nullptr); + code += + "MANUALLY_ALIGNED_STRUCT(" + NumToString(struct_def.minalign) + ") "; + code += struct_def.name + " FLATBUFFERS_FINAL_CLASS {\n private:\n"; + int padding_id = 0; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + code += " " + GenTypeGet(field.value.type, " ", "", " ", false); + code += field.name + "_;\n"; + GenPadding(field, code, padding_id, PaddingDefinition); + } + + // Generate GetFullyQualifiedName + code += "\n public:\n"; + GenFullyQualifiedNameGetter(struct_def.name, code); + + // Generate a constructor that takes all fields as arguments. + code += " " + struct_def.name + "("; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (it != struct_def.fields.vec.begin()) code += ", "; + code += GenTypeGet(field.value.type, " ", "const ", " &", true); + code += "_" + field.name; + } + code += ")\n : "; + padding_id = 0; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (it != struct_def.fields.vec.begin()) code += ", "; + code += field.name + "_("; + if (IsScalar(field.value.type.base_type)) { + code += "flatbuffers::EndianScalar("; + code += GenUnderlyingCast(field, false, "_" + field.name); + code += "))"; + } else { + code += "_" + field.name + ")"; + } + GenPadding(field, code, padding_id, PaddingInitializer); + } + + code += " {"; + padding_id = 0; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + GenPadding(field, code, padding_id, PaddingDeclaration); + } + code += " }\n\n"; + + // Generate accessor methods of the form: + // type name() const { return flatbuffers::EndianScalar(name_); } + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + GenComment(field.doc_comment, code_ptr, nullptr, " "); + auto is_scalar = IsScalar(field.value.type.base_type); + code += " " + + GenTypeGet(field.value.type, " ", "const ", " &", true); + code += field.name + "() const { return "; + code += GenUnderlyingCast( + field, true, is_scalar + ? "flatbuffers::EndianScalar(" + field.name + "_)" + : field.name + "_"); + code += "; }\n"; + if (parser_.opts.mutable_buffer) { + if (is_scalar) { + code += " void mutate_" + field.name + "("; + code += GenTypeBasic(field.value.type, true); + code += " _" + field.name + ") { flatbuffers::WriteScalar(&"; + code += field.name + "_, "; + code += GenUnderlyingCast(field, false, "_" + field.name); + code += "); }\n"; + } else { + code += " "; + code += GenTypeGet(field.value.type, "", "", " &", true); + code += "mutable_" + field.name + "() { return " + field.name; + code += "_; }\n"; + } + } + } + code += "};\nSTRUCT_END(" + struct_def.name + ", "; + code += NumToString(struct_def.bytesize) + ");\n\n"; + } + + void GenerateNestedNameSpaces(const Namespace *ns, std::string *code_ptr) { + for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { + *code_ptr += "namespace " + *it + " {\n"; + } + } + + void CloseNestedNameSpaces(const Namespace *ns, std::string *code_ptr) { + for (auto it = ns->components.rbegin(); it != ns->components.rend(); ++it) { + *code_ptr += "} // namespace " + *it + "\n"; + } + } + + void CheckNameSpace(const Definition &def, std::string *code_ptr) { + // Set up the correct namespace. Only open a namespace if + // the existing one is different. + // TODO: this could be done more intelligently, by sorting to + // namespace path and only opening/closing what is necessary, but that's + // quite a bit more complexity. + if (code_generator_cur_name_space != def.defined_namespace) { + if (code_generator_cur_name_space) { + CloseNestedNameSpaces(code_generator_cur_name_space, code_ptr); + if (code_generator_cur_name_space->components.size()) *code_ptr += "\n"; + } + GenerateNestedNameSpaces(def.defined_namespace, code_ptr); + code_generator_cur_name_space = def.defined_namespace; + if (code_generator_cur_name_space->components.size()) *code_ptr += "\n"; + } + } }; } // namespace cpp diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 594c6f7c8..3da41faef 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -22,8 +22,8 @@ enum Color { Color_Red = 1, Color_Green = 2, Color_Blue = 8, - Color_MIN = Color_Red, - Color_MAX = Color_Blue + Color_NONE = 0, + Color_ANY = 11 }; inline const char **EnumNamesColor() { From a3a2bf890f9e02f3f0f5f28f2e07edb630419707 Mon Sep 17 00:00:00 2001 From: lakedaemon Date: Thu, 2 Jun 2016 23:01:40 +0200 Subject: [PATCH 2/4] Better C++ namespace management --- src/idl_gen_cpp.cpp | 72 ++++++++----------- .../namespace_test2_generated.h | 4 -- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 875345933..f3a9f1254 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -86,7 +86,7 @@ class CppGenerator : public BaseGenerator { if (num_includes) code += "\n"; } - assert(!code_generator_cur_name_space); + assert(!cur_name_space_); // Generate forward declarations for all structs/tables, since they may // have circular references. @@ -94,7 +94,7 @@ class CppGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (!struct_def.generated) { - CheckNameSpace(struct_def, &code); + CheckNameSpace(struct_def.defined_namespace, &code); code += "struct " + struct_def.name + ";\n\n"; } } @@ -104,7 +104,7 @@ class CppGenerator : public BaseGenerator { ++it) { auto &enum_def = **it; if (!enum_def.generated) { - CheckNameSpace(**it, &code); + CheckNameSpace((**it).defined_namespace, &code); GenEnum(**it, &code); } } @@ -114,7 +114,7 @@ class CppGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (struct_def.fixed && !struct_def.generated) { - CheckNameSpace(struct_def, &code); + CheckNameSpace(struct_def.defined_namespace, &code); GenStruct(struct_def, &code); } } @@ -122,7 +122,7 @@ class CppGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (!struct_def.fixed && !struct_def.generated) { - CheckNameSpace(struct_def, &code); + CheckNameSpace(struct_def.defined_namespace, &code); GenTable(struct_def, &code); } } @@ -132,14 +132,14 @@ class CppGenerator : public BaseGenerator { ++it) { auto &enum_def = **it; if (enum_def.is_union && !enum_def.generated) { - CheckNameSpace(enum_def, &code); + CheckNameSpace(enum_def.defined_namespace, &code); GenEnumPost(enum_def, &code); } } // Generate convenient global helper functions: if (parser_.root_struct_def_) { - CheckNameSpace(*parser_.root_struct_def_, &code); + CheckNameSpace((*parser_.root_struct_def_).defined_namespace, &code); auto &name = parser_.root_struct_def_->name; std::string qualified_name = parser_.namespaces_.back()->GetFullyQualifiedName(name); @@ -195,25 +195,23 @@ class CppGenerator : public BaseGenerator { code += "); }\n\n"; } - assert(code_generator_cur_name_space); - CloseNestedNameSpaces(code_generator_cur_name_space, &code); - - code_generator_cur_name_space = nullptr; + assert(cur_name_space_); + CheckNameSpace(nullptr, &code); // Close the include guard. - code += "\n#endif // " + include_guard + "\n"; + code += "#endif // " + include_guard + "\n"; return SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false); } private: // This tracks the current namespace so we can insert namespace declarations. - const Namespace *code_generator_cur_name_space = nullptr; + const Namespace *cur_name_space_ = nullptr; // Ensure that a type is prefixed with its namespace whenever it is used // outside of its namespace. std::string WrapInNameSpace(const Namespace *ns, const std::string &name) { - if (code_generator_cur_name_space != ns) { + if (cur_name_space_ != ns) { std::string qualified_name; for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { qualified_name += *it + "::"; @@ -863,33 +861,25 @@ class CppGenerator : public BaseGenerator { code += NumToString(struct_def.bytesize) + ");\n\n"; } - void GenerateNestedNameSpaces(const Namespace *ns, std::string *code_ptr) { - for (auto it = ns->components.begin(); it != ns->components.end(); ++it) { - *code_ptr += "namespace " + *it + " {\n"; - } - } - - void CloseNestedNameSpaces(const Namespace *ns, std::string *code_ptr) { - for (auto it = ns->components.rbegin(); it != ns->components.rend(); ++it) { - *code_ptr += "} // namespace " + *it + "\n"; - } - } - - void CheckNameSpace(const Definition &def, std::string *code_ptr) { - // Set up the correct namespace. Only open a namespace if - // the existing one is different. - // TODO: this could be done more intelligently, by sorting to - // namespace path and only opening/closing what is necessary, but that's - // quite a bit more complexity. - if (code_generator_cur_name_space != def.defined_namespace) { - if (code_generator_cur_name_space) { - CloseNestedNameSpaces(code_generator_cur_name_space, code_ptr); - if (code_generator_cur_name_space->components.size()) *code_ptr += "\n"; - } - GenerateNestedNameSpaces(def.defined_namespace, code_ptr); - code_generator_cur_name_space = def.defined_namespace; - if (code_generator_cur_name_space->components.size()) *code_ptr += "\n"; - } + // Set up the correct namespace. Only open a namespace if + // the existing one is different (opening/closing only what is necessary) + // the file must start and end with an empty namespace + void CheckNameSpace(const Namespace *ns, std::string *code_ptr) { + if (cur_name_space_ == ns) return; + auto s1 = cur_name_space_ == nullptr ? 0 : cur_name_space_->components.size(); + auto s2 = ns == nullptr ? 0 : ns->components.size(); + std::vector::size_type limit = 0; + while (limit < s1 && limit < s2 && + ns->components[limit] == cur_name_space_->components[limit]) + limit++; + for (auto j = s1; j > limit; --j) + *code_ptr += + "} // namespace " + cur_name_space_->components[j - 1] + "\n"; + if (s1 != limit) *code_ptr += "\n"; + for (auto j = limit; j != s2; ++j) + *code_ptr += "namespace " + ns->components[j] + " {\n"; + if (s2 != limit) *code_ptr += "\n"; + cur_name_space_ = ns; } }; } // namespace cpp diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index d0cc5fab2..8e6b3ada8 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -23,10 +23,6 @@ namespace NamespaceA { struct SecondTableInA; -} // namespace NamespaceA - -namespace NamespaceA { - struct TableInFirstNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_FOO_TABLE = 4, From 15863c34a0733929069da177aab2861ddb719e80 Mon Sep 17 00:00:00 2001 From: lakedaemon Date: Tue, 7 Jun 2016 14:46:25 +0200 Subject: [PATCH 3/4] SetNameSpace algorithm documentation and more meaningful variable names --- src/idl_gen_cpp.cpp | 52 ++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index f3a9f1254..b1402d8e6 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -94,7 +94,7 @@ class CppGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (!struct_def.generated) { - CheckNameSpace(struct_def.defined_namespace, &code); + SetNameSpace(struct_def.defined_namespace, &code); code += "struct " + struct_def.name + ";\n\n"; } } @@ -104,7 +104,7 @@ class CppGenerator : public BaseGenerator { ++it) { auto &enum_def = **it; if (!enum_def.generated) { - CheckNameSpace((**it).defined_namespace, &code); + SetNameSpace((**it).defined_namespace, &code); GenEnum(**it, &code); } } @@ -114,7 +114,7 @@ class CppGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (struct_def.fixed && !struct_def.generated) { - CheckNameSpace(struct_def.defined_namespace, &code); + SetNameSpace(struct_def.defined_namespace, &code); GenStruct(struct_def, &code); } } @@ -122,7 +122,7 @@ class CppGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; if (!struct_def.fixed && !struct_def.generated) { - CheckNameSpace(struct_def.defined_namespace, &code); + SetNameSpace(struct_def.defined_namespace, &code); GenTable(struct_def, &code); } } @@ -132,14 +132,14 @@ class CppGenerator : public BaseGenerator { ++it) { auto &enum_def = **it; if (enum_def.is_union && !enum_def.generated) { - CheckNameSpace(enum_def.defined_namespace, &code); + SetNameSpace(enum_def.defined_namespace, &code); GenEnumPost(enum_def, &code); } } // Generate convenient global helper functions: if (parser_.root_struct_def_) { - CheckNameSpace((*parser_.root_struct_def_).defined_namespace, &code); + SetNameSpace((*parser_.root_struct_def_).defined_namespace, &code); auto &name = parser_.root_struct_def_->name; std::string qualified_name = parser_.namespaces_.back()->GetFullyQualifiedName(name); @@ -196,7 +196,7 @@ class CppGenerator : public BaseGenerator { } assert(cur_name_space_); - CheckNameSpace(nullptr, &code); + SetNameSpace(nullptr, &code); // Close the include guard. code += "#endif // " + include_guard + "\n"; @@ -862,23 +862,35 @@ class CppGenerator : public BaseGenerator { } // Set up the correct namespace. Only open a namespace if - // the existing one is different (opening/closing only what is necessary) - // the file must start and end with an empty namespace - void CheckNameSpace(const Namespace *ns, std::string *code_ptr) { + // 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, std::string *code_ptr) { if (cur_name_space_ == ns) return; - auto s1 = cur_name_space_ == nullptr ? 0 : cur_name_space_->components.size(); - auto s2 = ns == nullptr ? 0 : ns->components.size(); - std::vector::size_type limit = 0; - while (limit < s1 && limit < s2 && - ns->components[limit] == cur_name_space_->components[limit]) - limit++; - for (auto j = s1; j > limit; --j) + // 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 + auto old_size = + cur_name_space_ == nullptr ? 0 : cur_name_space_->components.size(); + auto new_size = ns == nullptr ? 0 : ns->components.size(); + std::vector::size_type 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 (auto j = old_size; j > common_prefix_size; --j) *code_ptr += "} // namespace " + cur_name_space_->components[j - 1] + "\n"; - if (s1 != limit) *code_ptr += "\n"; - for (auto j = limit; j != s2; ++j) + if (old_size != common_prefix_size) *code_ptr += "\n"; + // 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_ptr += "namespace " + ns->components[j] + " {\n"; - if (s2 != limit) *code_ptr += "\n"; + if (new_size != common_prefix_size) *code_ptr += "\n"; cur_name_space_ = ns; } }; From 3ae503a19194666eb56a78f5b5cbb106e2d201f3 Mon Sep 17 00:00:00 2001 From: Alexander Gallego Date: Tue, 7 Jun 2016 18:08:09 -0400 Subject: [PATCH 4/4] Add env var for flatc schema compiler cmake module --- CMake/BuildFlatBuffers.cmake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CMake/BuildFlatBuffers.cmake b/CMake/BuildFlatBuffers.cmake index 2c28899a7..42f78de77 100644 --- a/CMake/BuildFlatBuffers.cmake +++ b/CMake/BuildFlatBuffers.cmake @@ -63,6 +63,13 @@ function(build_flatbuffers flatbuffers_schemas set(FLATC_TARGET flatc) set(FLATC flatc) endif() + set(FLATC_SCHEMA_ARGS --gen-mutable) + if(FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS) + set(FLATC_SCHEMA_ARGS + ${FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS} + ${FLATC_SCHEMA_ARGS} + ) + endif() set(schema_glob "*.fbs") # Generate the include files parameters. @@ -86,7 +93,7 @@ function(build_flatbuffers flatbuffers_schemas set(generated_include ${generated_includes_dir}/${filename}_generated.h) add_custom_command( OUTPUT ${generated_include} - COMMAND ${FLATC} --gen-mutable + COMMAND ${FLATC} ${FLATC_SCHEMA_ARGS} -o ${generated_includes_dir} ${include_params} -c ${schema}