Add --gen-absl-hash option to generate AbslHashValue for structs. (#8868)
diff --git a/CMakeLists.txt b/CMakeLists.txt index d7c835c..9e39c49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -571,8 +571,6 @@ # 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 $<BUILD_INTERFACE:ProjectConfig> flatsample) target_link_libraries(flatsampletext PRIVATE $<BUILD_INTERFACE:ProjectConfig> flatsample)
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 4dda9ba..3e35b48 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h
@@ -664,6 +664,7 @@ 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 @@ 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 a0facac..a618cbf 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h
@@ -236,6 +236,10 @@ return !(lhs == rhs); } +template <typename H> +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 68b9869..09ebf88 100755 --- a/scripts/generate_code.py +++ b/scripts/generate_code.py
@@ -69,6 +69,7 @@ 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 761af1e..4bb590b 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp
@@ -529,6 +529,8 @@ 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 f056727..54706b0 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp
@@ -2311,6 +2311,34 @@ code_ += ""; } + void GenAbslHashValue(const StructDef& struct_def) { + code_ += "template <typename H>"; + code_ += "inline H AbslHashValue(H h, const {{NATIVE_NAME}} &obj) {"; + std::string combine_args; + for (const auto& field : struct_def.fields.vec) { + if (field->deprecated) { + continue; + } + if (IsArray(field->value.type)) { + const auto field_accessor = "obj." + Name(*field) + "()"; + const auto& element_type = field->value.type.VectorType(); + if (IsStruct(element_type)) { + code_ += " for (const auto* element : *" + field_accessor + ") {"; + code_ += " h = H::combine(std::move(h), *element);"; + code_ += " }"; + } else { + code_ += " for (auto element : *" + field_accessor + ") {"; + code_ += " h = H::combine(std::move(h), element);"; + code_ += " }"; + } + } else { + combine_args += ", obj." + Name(*field) + "()"; + } + } + code_ += " return H::combine(std::move(h)" + combine_args + ");"; + code_ += "}"; + } + void GenOperatorNewDelete(const StructDef& struct_def) { if (auto native_custom_alloc = struct_def.attributes.Lookup("native_custom_alloc")) { @@ -4281,7 +4309,12 @@ 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 9aecdf7..d95eac8 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel
@@ -213,6 +213,7 @@ 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 827d058..349048c 100644 --- a/tests/evolution_test/evolution_v1_generated.h +++ b/tests/evolution_test/evolution_v1_generated.h
@@ -145,6 +145,10 @@ return !(lhs == rhs); } +template <typename H> +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 885830d..baa678f 100644 --- a/tests/evolution_test/evolution_v2_generated.h +++ b/tests/evolution_test/evolution_v2_generated.h
@@ -161,6 +161,10 @@ return !(lhs == rhs); } +template <typename H> +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 594670b..81893af 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h
@@ -712,6 +712,10 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 b3db30d..b6bf2e9 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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 b3db30d..b6bf2e9 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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 b3db30d..b6bf2e9 100644 --- a/tests/monster_test_suffix/monster_test_suffix.hpp +++ b/tests/monster_test_suffix/monster_test_suffix.hpp
@@ -709,6 +709,10 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 ccd4527..05e2f33 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 @@ 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<ArrayStruct> 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 @@ 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 1117efd..1fd25b0 100644 --- a/tests/union_vector/union_vector_generated.h +++ b/tests/union_vector/union_vector_generated.h
@@ -384,6 +384,10 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +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 @@ return !(lhs == rhs); } +template <typename H> +inline H AbslHashValue(H h, const FallingTub &obj) { + return H::combine(std::move(h), obj.weight()); +} struct AttackerT : public ::flatbuffers::NativeTable { typedef Attacker TableType;