diff --git a/CMakeLists.txt b/CMakeLists.txt index d7c835c66..9e39c49cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -571,8 +571,6 @@ if(FLATBUFFERS_BUILD_TESTS) # Since flatsample has no sources, we have to explicitly set the linker lang. set_target_properties(flatsample PROPERTIES LINKER_LANGUAGE CXX) - - compile_schema_for_samples(samples/monster.fbs "${FLATC_OPT_COMP}") target_link_libraries(flatsamplebinary PRIVATE $ flatsample) target_link_libraries(flatsampletext PRIVATE $ flatsample) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 4dda9bac1..3e35b48be 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -664,6 +664,7 @@ struct IDLOptions { bool generate_name_strings; bool generate_object_based_api; bool gen_compare; + bool gen_absl_hash; std::string cpp_object_api_pointer_type; std::string cpp_object_api_string_type; bool cpp_object_api_string_flexible_constructor; @@ -818,6 +819,7 @@ struct IDLOptions { generate_name_strings(false), generate_object_based_api(false), gen_compare(false), + gen_absl_hash(false), cpp_object_api_pointer_type("std::unique_ptr"), cpp_object_api_string_flexible_constructor(false), cpp_object_api_field_case_style(CaseStyle_Unchanged), diff --git a/samples/monster_generated.h b/samples/monster_generated.h index a0facac96..a618cbf4d 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -236,6 +236,10 @@ inline bool operator!=(const Vec3 &lhs, const Vec3 &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Vec3 &obj) { + return H::combine(std::move(h), obj.x(), obj.y(), obj.z()); +} struct MonsterT : public ::flatbuffers::NativeTable { typedef Monster TableType; diff --git a/scripts/generate_code.py b/scripts/generate_code.py index 68b986944..09ebf88de 100755 --- a/scripts/generate_code.py +++ b/scripts/generate_code.py @@ -69,6 +69,7 @@ CS_OPTS = ["--csharp", "--cs-gen-json-serializer"] CPP_OPTS = [ "--cpp", "--gen-compare", + "--gen-absl-hash", ] + (["--cpp-std", "c++0x"] if args.cpp_0x else []) CPP_17_OPTS = NO_INCL_OPTS + [ diff --git a/src/flatc.cpp b/src/flatc.cpp index 761af1e22..4bb590b5f 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -529,6 +529,8 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc, opts.generate_object_based_api = true; } else if (arg == "--gen-compare") { opts.gen_compare = true; + } else if (arg == "--gen-absl-hash") { + opts.gen_absl_hash = true; } else if (arg == "--cpp-include") { if (++argi >= argc) Error("missing include following: " + arg, true); opts.cpp_includes.push_back(argv[argi]); diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index f056727a1..54706b037 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2311,6 +2311,34 @@ class CppGenerator : public BaseGenerator { code_ += ""; } + void GenAbslHashValue(const StructDef& struct_def) { + code_ += "template "; + 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")) { @@ -4281,7 +4309,12 @@ class CppGenerator : public BaseGenerator { 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_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- diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 9aecdf746..d95eac83c 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -213,6 +213,7 @@ flatbuffer_cc_library( include_test_args = [ "--gen-object-api", "--gen-compare", + "--gen-absl-hash", "--gen-mutable", "--reflect-names", "--cpp-ptr-type flatbuffers::unique_ptr", diff --git a/tests/evolution_test/evolution_v1_generated.h b/tests/evolution_test/evolution_v1_generated.h index 827d058ae..349048c32 100644 --- a/tests/evolution_test/evolution_v1_generated.h +++ b/tests/evolution_test/evolution_v1_generated.h @@ -145,6 +145,10 @@ inline bool operator!=(const Struct &lhs, const Struct &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Struct &obj) { + return H::combine(std::move(h), obj.a(), obj.b()); +} struct TableA FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef TableABuilder Builder; diff --git a/tests/evolution_test/evolution_v2_generated.h b/tests/evolution_test/evolution_v2_generated.h index 885830d4c..baa678f06 100644 --- a/tests/evolution_test/evolution_v2_generated.h +++ b/tests/evolution_test/evolution_v2_generated.h @@ -161,6 +161,10 @@ inline bool operator!=(const Struct &lhs, const Struct &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Struct &obj) { + return H::combine(std::move(h), obj.a(), obj.b()); +} struct TableA FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef TableABuilder Builder; diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 594670baa..81893af0e 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -712,6 +712,10 @@ inline bool operator!=(const Test &lhs, const Test &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Test &obj) { + return H::combine(std::move(h), obj.a(), obj.b()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { private: @@ -810,6 +814,10 @@ inline bool operator!=(const Vec3 &lhs, const Vec3 &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Vec3 &obj) { + return H::combine(std::move(h), obj.x(), obj.y(), obj.z(), obj.test1(), obj.test2(), obj.test3()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { private: @@ -859,6 +867,10 @@ inline bool operator!=(const Ability &lhs, const Ability &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Ability &obj) { + return H::combine(std::move(h), obj.id(), obj.distance()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -912,6 +924,10 @@ inline bool operator!=(const StructOfStructs &lhs, const StructOfStructs &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructs &obj) { + return H::combine(std::move(h), obj.a(), obj.b(), obj.c()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructsOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -945,6 +961,10 @@ inline bool operator!=(const StructOfStructsOfStructs &lhs, const StructOfStruct return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructsOfStructs &obj) { + return H::combine(std::move(h), obj.a()); +} } // namespace Example diff --git a/tests/monster_test_suffix/ext_only/monster_test_generated.hpp b/tests/monster_test_suffix/ext_only/monster_test_generated.hpp index b3db30d3b..b6bf2e93b 100644 --- a/tests/monster_test_suffix/ext_only/monster_test_generated.hpp +++ b/tests/monster_test_suffix/ext_only/monster_test_generated.hpp @@ -709,6 +709,10 @@ inline bool operator!=(const Test &lhs, const Test &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Test &obj) { + return H::combine(std::move(h), obj.a(), obj.b()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { private: @@ -807,6 +811,10 @@ inline bool operator!=(const Vec3 &lhs, const Vec3 &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Vec3 &obj) { + return H::combine(std::move(h), obj.x(), obj.y(), obj.z(), obj.test1(), obj.test2(), obj.test3()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { private: @@ -856,6 +864,10 @@ inline bool operator!=(const Ability &lhs, const Ability &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Ability &obj) { + return H::combine(std::move(h), obj.id(), obj.distance()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -909,6 +921,10 @@ inline bool operator!=(const StructOfStructs &lhs, const StructOfStructs &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructs &obj) { + return H::combine(std::move(h), obj.a(), obj.b(), obj.c()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructsOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -942,6 +958,10 @@ inline bool operator!=(const StructOfStructsOfStructs &lhs, const StructOfStruct return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructsOfStructs &obj) { + return H::combine(std::move(h), obj.a()); +} } // namespace Example diff --git a/tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h b/tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h index b3db30d3b..b6bf2e93b 100644 --- a/tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h +++ b/tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h @@ -709,6 +709,10 @@ inline bool operator!=(const Test &lhs, const Test &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Test &obj) { + return H::combine(std::move(h), obj.a(), obj.b()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { private: @@ -807,6 +811,10 @@ inline bool operator!=(const Vec3 &lhs, const Vec3 &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Vec3 &obj) { + return H::combine(std::move(h), obj.x(), obj.y(), obj.z(), obj.test1(), obj.test2(), obj.test3()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { private: @@ -856,6 +864,10 @@ inline bool operator!=(const Ability &lhs, const Ability &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Ability &obj) { + return H::combine(std::move(h), obj.id(), obj.distance()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -909,6 +921,10 @@ inline bool operator!=(const StructOfStructs &lhs, const StructOfStructs &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructs &obj) { + return H::combine(std::move(h), obj.a(), obj.b(), obj.c()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructsOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -942,6 +958,10 @@ inline bool operator!=(const StructOfStructsOfStructs &lhs, const StructOfStruct return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructsOfStructs &obj) { + return H::combine(std::move(h), obj.a()); +} } // namespace Example diff --git a/tests/monster_test_suffix/monster_test_suffix.hpp b/tests/monster_test_suffix/monster_test_suffix.hpp index b3db30d3b..b6bf2e93b 100644 --- a/tests/monster_test_suffix/monster_test_suffix.hpp +++ b/tests/monster_test_suffix/monster_test_suffix.hpp @@ -709,6 +709,10 @@ inline bool operator!=(const Test &lhs, const Test &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Test &obj) { + return H::combine(std::move(h), obj.a(), obj.b()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Vec3 FLATBUFFERS_FINAL_CLASS { private: @@ -807,6 +811,10 @@ inline bool operator!=(const Vec3 &lhs, const Vec3 &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Vec3 &obj) { + return H::combine(std::move(h), obj.x(), obj.y(), obj.z(), obj.test1(), obj.test2(), obj.test3()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Ability FLATBUFFERS_FINAL_CLASS { private: @@ -856,6 +864,10 @@ inline bool operator!=(const Ability &lhs, const Ability &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Ability &obj) { + return H::combine(std::move(h), obj.id(), obj.distance()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -909,6 +921,10 @@ inline bool operator!=(const StructOfStructs &lhs, const StructOfStructs &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructs &obj) { + return H::combine(std::move(h), obj.a(), obj.b(), obj.c()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StructOfStructsOfStructs FLATBUFFERS_FINAL_CLASS { private: @@ -942,6 +958,10 @@ inline bool operator!=(const StructOfStructsOfStructs &lhs, const StructOfStruct return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const StructOfStructsOfStructs &obj) { + return H::combine(std::move(h), obj.a()); +} } // namespace Example diff --git a/tests/test.cpp b/tests/test.cpp index ccd452738..05e2f3345 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -26,6 +26,13 @@ #define INCLUDE_64_BIT_TESTS 1 #endif +#if __has_include("third_party/absl/container/flat_hash_set.h") +#define HAS_ABSL_CONTAINERS 1 +#endif + +#ifdef HAS_ABSL_CONTAINERS +#include "third_party/absl/container/flat_hash_set.h" +#endif #include "alignment_test.h" #include "evolution_test.h" #include "flatbuffers/flatbuffers.h" @@ -1665,6 +1672,40 @@ void UnionUnderlyingTypeTest() { TEST_ASSERT(unpacked.test_vector_of_union == buffer.test_vector_of_union); } +void StructsInHashTableTest() { +#if defined(HAS_ABSL_CONTAINERS) && (!defined(_MSC_VER) || _MSC_VER >= 1700) + absl::flat_hash_set hash_set; + ArrayStruct array_struct_1; + array_struct_1.mutate_a(0.4); + for (int i = 0; i < array_struct_1.b()->size(); ++i) { + array_struct_1.mutable_b()->Mutate(i, i * 2); + } + for (int i = 0; i < array_struct_1.d()->size(); ++i) { + NestedStruct nested_struct; + nested_struct.mutable_a()->Mutate(0, i * 3); + array_struct_1.mutable_d()->Mutate(i, nested_struct); + } + + ArrayStruct array_struct_2; + array_struct_2.mutate_e(999); + + hash_set.insert(array_struct_1); + hash_set.insert(array_struct_2); + + TEST_EQ(hash_set.size(), 2); + TEST_ASSERT(hash_set.contains(array_struct_1)); + TEST_ASSERT(hash_set.contains(array_struct_2)); + + ArrayStruct array_struct_3 = array_struct_1; + array_struct_3.mutable_b()->Mutate(0, 2); + TEST_ASSERT(!hash_set.contains(array_struct_3)); + + hash_set.insert(array_struct_3); + TEST_ASSERT(hash_set.contains(array_struct_3)); +#endif // defined(HAS_ABSL_CONTAINERS) && (!defined(_MSC_VER) || _MSC_VER >= + // 1700) +} + static void Offset64Tests() { #if INCLUDE_64_BIT_TESTS Offset64Test(); @@ -1794,6 +1835,7 @@ int FlatBufferTests(const std::string& tests_data_path) { EmbeddedSchemaAccess(); Offset64Tests(); UnionUnderlyingTypeTest(); + StructsInHashTableTest(); return 0; } } // namespace diff --git a/tests/union_vector/union_vector_generated.h b/tests/union_vector/union_vector_generated.h index 1117efd80..1fd25b034 100644 --- a/tests/union_vector/union_vector_generated.h +++ b/tests/union_vector/union_vector_generated.h @@ -384,6 +384,10 @@ inline bool operator!=(const Rapunzel &lhs, const Rapunzel &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const Rapunzel &obj) { + return H::combine(std::move(h), obj.hair_length()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) BookReader FLATBUFFERS_FINAL_CLASS { private: @@ -417,6 +421,10 @@ inline bool operator!=(const BookReader &lhs, const BookReader &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const BookReader &obj) { + return H::combine(std::move(h), obj.books_read()); +} FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) FallingTub FLATBUFFERS_FINAL_CLASS { private: @@ -450,6 +458,10 @@ inline bool operator!=(const FallingTub &lhs, const FallingTub &rhs) { return !(lhs == rhs); } +template +inline H AbslHashValue(H h, const FallingTub &obj) { + return H::combine(std::move(h), obj.weight()); +} struct AttackerT : public ::flatbuffers::NativeTable { typedef Attacker TableType;