From b63ebad49dc0ff3456c787f9b689144f6e8860c7 Mon Sep 17 00:00:00 2001 From: Chandra Penke Date: Wed, 6 Jan 2016 08:31:53 -0800 Subject: [PATCH] Fix #3497: Add support for compiling in g++ 4.4 and 4.5 - Removed uses of lambda expressions - Added custom defines for constexpr and nullptr - Removed trailing comma of last value from generated enums --- include/flatbuffers/flatbuffers.h | 48 ++++++++--- include/flatbuffers/hash.h | 6 +- include/flatbuffers/idl.h | 4 + include/flatbuffers/util.h | 8 +- samples/monster_generated.h | 2 +- src/flatc.cpp | 1 + src/idl_gen_cpp.cpp | 57 +++++++++---- src/idl_parser.cpp | 85 ++++++++++--------- tests/monster_test_generated.h | 6 +- .../namespace_test1_generated.h | 2 +- .../namespace_test2_generated.h | 2 +- tests/test.cpp | 35 +++++--- 12 files changed, 166 insertions(+), 90 deletions(-) diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index b6b84030f..65790b822 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -33,11 +33,30 @@ #if __cplusplus <= 199711L && \ (!defined(_MSC_VER) || _MSC_VER < 1600) && \ (!defined(__GNUC__) || \ - (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40603)) - #error A C++11 compatible compiler is required for FlatBuffers. + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400)) + #error A C++11 compatible compiler with support for the auto typing is required for FlatBuffers. #error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ #endif +#if !defined(__clang__) && \ + defined(__GNUC__) && \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600) + // Backwards compatability for g++ 4.4, and 4.5 which don't have the nullptr and constexpr + // keywords. Note the __clang__ check is needed, because clang presents itself as an older GNUC + // compiler. + #ifndef nullptr_t + const class nullptr_t { + public: + template inline operator T*() const { return 0; } + private: + void operator&() const; + } nullptr = {}; + #endif + #ifndef constexpr + #define constexpr const + #endif +#endif + // The wire format uses a little endian encoding (since that's efficient for // the common platforms). #if !defined(FLATBUFFERS_LITTLEENDIAN) @@ -154,7 +173,11 @@ template size_t AlignOf() { #ifdef _MSC_VER return __alignof(T); #else - return alignof(T); + #ifndef alignof + return __alignof__(T); + #else + return alignof(T); + #endif #endif } @@ -836,15 +859,20 @@ class FlatBufferBuilder FLATBUFFERS_FINAL_CLASS { return CreateVectorOfStructs(v.data(), v.size()); } + template + struct TableKeyComparator { + TableKeyComparator(vector_downward& buf) : buf_(buf) {} + bool operator()(const Offset &a, const Offset &b) const { + auto table_a = reinterpret_cast(buf_.data_at(a.o)); + auto table_b = reinterpret_cast(buf_.data_at(b.o)); + return table_a->KeyCompareLessThan(table_b); + } + vector_downward& buf_; + }; + template Offset>> CreateVectorOfSortedTables( Offset *v, size_t len) { - std::sort(v, v + len, - [this](const Offset &a, const Offset &b) -> bool { - auto table_a = reinterpret_cast(buf_.data_at(a.o)); - auto table_b = reinterpret_cast(buf_.data_at(b.o)); - return table_a->KeyCompareLessThan(table_b); - } - ); + std::sort(v, v + len, TableKeyComparator(buf_)); return CreateVector(v, len); } diff --git a/include/flatbuffers/hash.h b/include/flatbuffers/hash.h index 134d17517..9ae37e5c0 100644 --- a/include/flatbuffers/hash.h +++ b/include/flatbuffers/hash.h @@ -20,6 +20,8 @@ #include #include +#include "flatbuffers/flatbuffers.h" + namespace flatbuffers { template @@ -36,8 +38,8 @@ struct FnvTraits { template <> struct FnvTraits { - static const uint64_t kFnvPrime = 0x00000100000001b3; - static const uint64_t kOffsetBasis = 0xcbf29ce484222645; + static const uint64_t kFnvPrime = 0x00000100000001b3ULL; + static const uint64_t kOffsetBasis = 0xcbf29ce484222645ULL; }; template diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index d052d6f4d..66ca8b5c1 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -491,6 +491,10 @@ private: FLATBUFFERS_CHECKED_ERROR DoParse(const char *_source, const char **include_paths, const char *source_filename); + FLATBUFFERS_CHECKED_ERROR CheckClash(std::vector &fields, + StructDef *struct_def, + const char *suffix, + BaseType baseType); public: SymbolTable structs_; diff --git a/include/flatbuffers/util.h b/include/flatbuffers/util.h index ba73d67bd..b9ce77dac 100644 --- a/include/flatbuffers/util.h +++ b/include/flatbuffers/util.h @@ -38,6 +38,8 @@ #include #endif +#include "flatbuffers/flatbuffers.h" + namespace flatbuffers { // Convert an integer or floating point value to a string. @@ -68,7 +70,7 @@ template<> inline std::string NumToString(double t) { auto p = s.find_last_not_of('0'); if (p != std::string::npos) { s.resize(p + 1); // Strip trailing zeroes. - if (s.back() == '.') + if (s[s.size() - 1] == '.') s.erase(s.size() - 1, 1); // Strip '.' if a whole number. } return s; @@ -197,8 +199,8 @@ inline std::string StripFileName(const std::string &filepath) { inline std::string ConCatPathFileName(const std::string &path, const std::string &filename) { std::string filepath = path; - if (path.length() && path.back() != kPathSeparator && - path.back() != kPosixPathSeparator) + if (path.length() && path[path.size() - 1] != kPathSeparator && + path[path.size() - 1] != kPosixPathSeparator) filepath += kPathSeparator; filepath += filename; return filepath; diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 53935ee93..2277b9738 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -65,7 +65,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_HP = 8, VT_NAME = 10, VT_INVENTORY = 14, - VT_COLOR = 16, + VT_COLOR = 16 }; const Vec3 *pos() const { return GetStruct(VT_POS); } Vec3 *mutable_pos() { return GetStruct(VT_POS); } diff --git a/src/flatc.cpp b/src/flatc.cpp index 48d98ce41..3bffa16fb 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -17,6 +17,7 @@ #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" #include "flatbuffers/util.h" +#include static void Error(const std::string &err, bool usage = false, bool show_exe_name = true); diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index fa2708000..bb6942073 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -267,16 +267,24 @@ static void GenTable(const Parser &parser, StructDef &struct_def, // 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) + ",\n"; + code += NumToString(field.value.offset); } } - code += " };\n"; + code += "\n };\n"; } // Generate the accessors. for (auto it = struct_def.fields.vec.begin(); @@ -513,15 +521,32 @@ static void GenTable(const Parser &parser, StructDef &struct_def, } static void GenPadding(const FieldDef &field, - const std::function &f) { + 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); + 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) { @@ -543,10 +568,7 @@ static void GenStruct(const Parser &parser, StructDef &struct_def, auto &field = **it; code += " " + GenTypeGet(parser, field.value.type, " ", "", " ", false); code += field.name + "_;\n"; - GenPadding(field, [&code, &padding_id](int bits) { - code += " int" + NumToString(bits) + - "_t __padding" + NumToString(padding_id++) + ";\n"; - }); + GenPadding(field, code, padding_id, PaddingDefinition); } // Generate a constructor that takes all fields as arguments. @@ -574,21 +596,16 @@ static void GenStruct(const Parser &parser, StructDef &struct_def, } else { code += "_" + field.name + ")"; } - GenPadding(field, [&code, &padding_id](int bits) { - (void)bits; - code += ", __padding" + NumToString(padding_id++) + "(0)"; - }); + 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](int bits) { - (void)bits; - code += " (void)__padding" + NumToString(padding_id++) + ";"; - }); + GenPadding(field, code, padding_id, PaddingDeclaration); } code += " }\n\n"; @@ -642,6 +659,12 @@ void CloseNestedNameSpaces(Namespace *ns, std::string *code_ptr) { } // namespace cpp +struct IsAlnum { + bool operator()(char c) { + return !isalnum(c); + } +}; + // Iterate through all definitions we haven't generate code for (enums, structs, // and tables) and output them to a single file. std::string GenerateCPP(const Parser &parser, @@ -712,7 +735,7 @@ std::string GenerateCPP(const Parser &parser, include_guard_ident.erase( std::remove_if(include_guard_ident.begin(), include_guard_ident.end(), - [](char c) { return !isalnum(c); }), + IsAlnum()), include_guard_ident.end()); std::string include_guard = "FLATBUFFERS_GENERATED_" + include_guard_ident; include_guard += "_"; diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index b533d213e..a1e96f663 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -1109,6 +1109,33 @@ CheckedError Parser::StartStruct(const std::string &name, StructDef **dest) { return NoError(); } +CheckedError Parser::CheckClash(std::vector &fields, + StructDef *struct_def, + const char *suffix, + BaseType basetype) { + auto len = strlen(suffix); + for (auto it = fields.begin(); it != fields.end(); ++it) { + auto &fname = (*it)->name; + if (fname.length() > len && + fname.compare(fname.length() - len, len, suffix) == 0 && + (*it)->value.type.base_type != BASE_TYPE_UTYPE) { + auto field = struct_def->fields.Lookup( + fname.substr(0, fname.length() - len)); + if (field && field->value.type.base_type == basetype) + return Error("Field " + fname + + " would clash with generated functions for field " + + field->name); + } + } + return NoError(); +} + +static bool compareFieldDefs(const FieldDef *a, const FieldDef *b) { + auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str()); + auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str()); + return a_id < b_id; +} + CheckedError Parser::ParseDecl() { std::vector dc = doc_comment_; bool fixed = Is(kTokenStruct); @@ -1151,12 +1178,7 @@ CheckedError Parser::ParseDecl() { "either all fields or no fields must have an 'id' attribute"); // Simply sort by id, then the fields are the same as if no ids had // been specified. - std::sort(fields.begin(), fields.end(), - [](const FieldDef *a, const FieldDef *b) -> bool { - auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str()); - auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str()); - return a_id < b_id; - }); + std::sort(fields.begin(), fields.end(), compareFieldDefs); // Verify we have a contiguous set, and reassign vtable offsets. for (int i = 0; i < static_cast(fields.size()); i++) { if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str())) @@ -1166,34 +1188,13 @@ CheckedError Parser::ParseDecl() { } } } - // Check that no identifiers clash with auto generated fields. - // This is not an ideal situation, but should occur very infrequently, - // and allows us to keep using very readable names for type & length fields - // without inducing compile errors. - auto CheckClash = [&fields, &struct_def, this](const char *suffix, - BaseType basetype) -> CheckedError { - auto len = strlen(suffix); - for (auto it = fields.begin(); it != fields.end(); ++it) { - auto &fname = (*it)->name; - if (fname.length() > len && - fname.compare(fname.length() - len, len, suffix) == 0 && - (*it)->value.type.base_type != BASE_TYPE_UTYPE) { - auto field = struct_def->fields.Lookup( - fname.substr(0, fname.length() - len)); - if (field && field->value.type.base_type == basetype) - return Error("Field " + fname + - " would clash with generated functions for field " + - field->name); - } - } - return NoError(); - }; - ECHECK(CheckClash("_type", BASE_TYPE_UNION)); - ECHECK(CheckClash("Type", BASE_TYPE_UNION)); - ECHECK(CheckClash("_length", BASE_TYPE_VECTOR)); - ECHECK(CheckClash("Length", BASE_TYPE_VECTOR)); - ECHECK(CheckClash("_byte_vector", BASE_TYPE_STRING)); - ECHECK(CheckClash("ByteVector", BASE_TYPE_STRING)); + + ECHECK(CheckClash(fields, struct_def, "_type", BASE_TYPE_UNION)); + ECHECK(CheckClash(fields, struct_def, "Type", BASE_TYPE_UNION)); + ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_VECTOR)); + ECHECK(CheckClash(fields, struct_def, "Length", BASE_TYPE_VECTOR)); + ECHECK(CheckClash(fields, struct_def, "_byte_vector", BASE_TYPE_STRING)); + ECHECK(CheckClash(fields, struct_def, "ByteVector", BASE_TYPE_STRING)); EXPECT('}'); return NoError(); } @@ -1235,6 +1236,10 @@ CheckedError Parser::ParseNamespace() { return NoError(); } +static bool compareEnumVals(const EnumVal *a, const EnumVal* b) { + return a->value < b->value; +} + // Best effort parsing of .proto declarations, with the aim to turn them // in the closest corresponding FlatBuffer equivalent. // We parse everything as identifiers instead of keywords, since we don't @@ -1285,9 +1290,8 @@ CheckedError Parser::ParseProtoDecl() { if (Is(';')) NEXT(); // Protobuf allows them to be specified in any order, so sort afterwards. auto &v = enum_def->vals.vec; - std::sort(v.begin(), v.end(), [](const EnumVal *a, const EnumVal *b) { - return a->value < b->value; - }); + std::sort(v.begin(), v.end(), compareEnumVals); + // Temp: remove any duplicates, as .fbs files can't handle them. for (auto it = v.begin(); it != v.end(); ) { if (it != v.begin() && it[0]->value == it[-1]->value) it = v.erase(it); @@ -1735,11 +1739,14 @@ std::set Parser::GetIncludedFilesRecursive( // Schema serialization functionality: +template bool compareName(const T* a, const T* b) { + return a->name < b->name; +} + template void AssignIndices(const std::vector &defvec) { // Pre-sort these vectors, such that we can set the correct indices for them. auto vec = defvec; - std::sort(vec.begin(), vec.end(), - [](const T *a, const T *b) { return a->name < b->name; }); + std::sort(vec.begin(), vec.end(), compareName); for (int i = 0; i < static_cast(vec.size()); i++) vec[i]->index = i; } diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 14780bf2d..5f0163f9e 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -98,7 +98,7 @@ STRUCT_END(Vec3, 32); struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { - VT_COLOR = 4, + VT_COLOR = 4 }; Color color() const { return static_cast(GetField(VT_COLOR, 2)); } bool mutate_color(Color _color) { return SetField(VT_COLOR, static_cast(_color)); } @@ -132,7 +132,7 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_ID = 4, VT_VAL = 6, - VT_COUNT = 8, + VT_COUNT = 8 }; const flatbuffers::String *id() const { return GetPointer(VT_ID); } flatbuffers::String *mutable_id() { return GetPointer(VT_ID); } @@ -201,7 +201,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_TESTHASHU32_FNV1A = 46, VT_TESTHASHS64_FNV1A = 48, VT_TESTHASHU64_FNV1A = 50, - VT_TESTARRAYOFBOOLS = 52, + VT_TESTARRAYOFBOOLS = 52 }; const Vec3 *pos() const { return GetStruct(VT_POS); } Vec3 *mutable_pos() { return GetStruct(VT_POS); } diff --git a/tests/namespace_test/namespace_test1_generated.h b/tests/namespace_test/namespace_test1_generated.h index 29f8e1229..e126f4e9d 100644 --- a/tests/namespace_test/namespace_test1_generated.h +++ b/tests/namespace_test/namespace_test1_generated.h @@ -43,7 +43,7 @@ STRUCT_END(StructInNestedNS, 8); struct TableInNestedNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { - VT_FOO = 4, + VT_FOO = 4 }; int32_t foo() const { return GetField(VT_FOO, 0); } bool mutate_foo(int32_t _foo) { return SetField(VT_FOO, _foo); } diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index f60986a61..5075894e1 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -22,7 +22,7 @@ struct TableInFirstNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_FOO_TABLE = 4, VT_FOO_ENUM = 6, - VT_FOO_STRUCT = 8, + VT_FOO_STRUCT = 8 }; const NamespaceA::NamespaceB::TableInNestedNS *foo_table() const { return GetPointer(VT_FOO_TABLE); } NamespaceA::NamespaceB::TableInNestedNS *mutable_foo_table() { return GetPointer(VT_FOO_TABLE); } diff --git a/tests/test.cpp b/tests/test.cpp index 83cdaef94..fce249e99 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -478,8 +478,8 @@ void FuzzTest1() { const uint16_t ushort_val = 0xFEEE; const int32_t int_val = 0x83333333; const uint32_t uint_val = 0xFDDDDDDD; - const int64_t long_val = 0x8444444444444444; - const uint64_t ulong_val = 0xFCCCCCCCCCCCCCCC; + const int64_t long_val = 0x8444444444444444LL; + const uint64_t ulong_val = 0xFCCCCCCCCCCCCCCCULL; const float float_val = 3.14159f; const double double_val = 3.14159265359; @@ -564,8 +564,28 @@ void FuzzTest2() { struct RndDef { std::string instances[instances_per_definition]; + + // Since we're generating schema and corresponding data in tandem, + // this convenience function adds strings to both at once. + static void Add(RndDef (&definitions_l)[num_definitions], + std::string &schema_l, + const int instances_per_definition_l, + const char *schema_add, const char *instance_add, + int definition) { + schema_l += schema_add; + for (int i = 0; i < instances_per_definition_l; i++) + definitions_l[definition].instances[i] += instance_add; + } }; + #define AddToSchemaAndInstances(schema_add, instance_add) \ + RndDef::Add(definitions, schema, instances_per_definition, \ + schema_add, instance_add, definition) + + #define Dummy() \ + RndDef::Add(definitions, schema, instances_per_definition, \ + "byte", "1", definition) + RndDef definitions[num_definitions]; // We are going to generate num_definitions, the first @@ -577,17 +597,6 @@ void FuzzTest2() { // being generated. We generate multiple instances such that when creating // hierarchy, we get some variety by picking one randomly. for (int definition = 0; definition < num_definitions; definition++) { - // Since we're generating schema & and corresponding data in tandem, - // this convenience function adds strings to both at once. - auto AddToSchemaAndInstances = [&](const char *schema_add, - const char *instance_add) { - schema += schema_add; - for (int i = 0; i < instances_per_definition; i++) - definitions[definition].instances[i] += instance_add; - }; - // Generate a default type if we can't generate something else. - auto Dummy = [&]() { AddToSchemaAndInstances("byte", "1"); }; - std::string definition_name = "D" + flatbuffers::NumToString(definition); bool is_struct = definition < num_struct_definitions;