diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 850a1e14e..612b40586 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -556,7 +556,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.name == rhs.name) && (lhs.inventory == rhs.inventory) && (lhs.color == rhs.color) && - (lhs.weapons == rhs.weapons) && + (lhs.weapons.size() == rhs.weapons.size() && std::equal(lhs.weapons.cbegin(), lhs.weapons.cend(), rhs.weapons.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.equipped == rhs.equipped) && (lhs.path == rhs.path); } diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index d581bcc95..2fa6409e6 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -2061,19 +2061,41 @@ class CppGenerator : public BaseGenerator { for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { const auto &field = **it; + const auto accessor = Name(field) + accessSuffix; + const auto lhs_accessor = "lhs." + accessor; + const auto rhs_accessor = "rhs." + accessor; + if (!field.deprecated && // Deprecated fields won't be accessible. field.value.type.base_type != BASE_TYPE_UTYPE && (field.value.type.base_type != BASE_TYPE_VECTOR || field.value.type.element != BASE_TYPE_UTYPE)) { if (!compare_op.empty()) { compare_op += " &&\n "; } - auto accessor = Name(field) + accessSuffix; if (struct_def.fixed || field.native_inline || field.value.type.base_type != BASE_TYPE_STRUCT) { - compare_op += "(lhs." + accessor + " == rhs." + accessor + ")"; + // If the field is a vector of tables, the table need to be compared + // by value, instead of by the default unique_ptr == operator which + // compares by address. + if (field.value.type.base_type == BASE_TYPE_VECTOR && + field.value.type.element == BASE_TYPE_STRUCT && + !field.value.type.struct_def->fixed) { + const auto type = + GenTypeNative(field.value.type.VectorType(), true, field); + const auto equal_length = + lhs_accessor + ".size() == " + rhs_accessor + ".size()"; + const auto elements_equal = + "std::equal(" + lhs_accessor + ".cbegin(), " + lhs_accessor + + ".cend(), " + rhs_accessor + ".cbegin(), [](" + type + + " const &a, " + type + + " const &b) { return (a == b) || (a && b && *a == *b); })"; + + compare_op += "(" + equal_length + " && " + elements_equal + ")"; + } else { + compare_op += "(" + lhs_accessor + " == " + rhs_accessor + ")"; + } } else { // Deep compare of std::unique_ptr. Null is not equal to empty. std::string both_null = - "(lhs." + accessor + " == rhs." + accessor + ")"; + "(" + lhs_accessor + " == " + rhs_accessor + ")"; std::string not_null_and_equal = "(lhs." + accessor + " && rhs." + accessor + " && *lhs." + accessor + " == *rhs." + accessor + ")"; diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 5e2308c7b..899567fa5 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -2727,7 +2727,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.test == rhs.test) && (lhs.test4 == rhs.test4) && (lhs.testarrayofstring == rhs.testarrayofstring) && - (lhs.testarrayoftables == rhs.testarrayoftables) && + (lhs.testarrayoftables.size() == rhs.testarrayoftables.size() && std::equal(lhs.testarrayoftables.cbegin(), lhs.testarrayoftables.cend(), rhs.testarrayoftables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && ((lhs.enemy == rhs.enemy) || (lhs.enemy && rhs.enemy && *lhs.enemy == *rhs.enemy)) && (lhs.testnestedflatbuffer == rhs.testnestedflatbuffer) && ((lhs.testempty == rhs.testempty) || (lhs.testempty && rhs.testempty && *lhs.testempty == *rhs.testempty)) && @@ -2751,10 +2751,10 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_longs == rhs.vector_of_longs) && (lhs.vector_of_doubles == rhs.vector_of_doubles) && ((lhs.parent_namespace_test == rhs.parent_namespace_test) || (lhs.parent_namespace_test && rhs.parent_namespace_test && *lhs.parent_namespace_test == *rhs.parent_namespace_test)) && - (lhs.vector_of_referrables == rhs.vector_of_referrables) && + (lhs.vector_of_referrables.size() == rhs.vector_of_referrables.size() && std::equal(lhs.vector_of_referrables.cbegin(), lhs.vector_of_referrables.cend(), rhs.vector_of_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.single_weak_reference == rhs.single_weak_reference) && (lhs.vector_of_weak_references == rhs.vector_of_weak_references) && - (lhs.vector_of_strong_referrables == rhs.vector_of_strong_referrables) && + (lhs.vector_of_strong_referrables.size() == rhs.vector_of_strong_referrables.size() && std::equal(lhs.vector_of_strong_referrables.cbegin(), lhs.vector_of_strong_referrables.cend(), rhs.vector_of_strong_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.co_owning_reference == rhs.co_owning_reference) && (lhs.vector_of_co_owning_references == rhs.vector_of_co_owning_references) && (lhs.non_owning_reference == rhs.non_owning_reference) && @@ -2764,7 +2764,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_enums == rhs.vector_of_enums) && (lhs.signed_enum == rhs.signed_enum) && (lhs.testrequirednestedflatbuffer == rhs.testrequirednestedflatbuffer) && - (lhs.scalar_key_sorted_tables == rhs.scalar_key_sorted_tables) && + (lhs.scalar_key_sorted_tables.size() == rhs.scalar_key_sorted_tables.size() && std::equal(lhs.scalar_key_sorted_tables.cbegin(), lhs.scalar_key_sorted_tables.cend(), rhs.scalar_key_sorted_tables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.native_inline == rhs.native_inline) && (lhs.long_enum_non_enum_default == rhs.long_enum_non_enum_default) && (lhs.long_enum_normal_default == rhs.long_enum_normal_default); 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 5e2308c7b..899567fa5 100644 --- a/tests/monster_test_suffix/ext_only/monster_test_generated.hpp +++ b/tests/monster_test_suffix/ext_only/monster_test_generated.hpp @@ -2727,7 +2727,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.test == rhs.test) && (lhs.test4 == rhs.test4) && (lhs.testarrayofstring == rhs.testarrayofstring) && - (lhs.testarrayoftables == rhs.testarrayoftables) && + (lhs.testarrayoftables.size() == rhs.testarrayoftables.size() && std::equal(lhs.testarrayoftables.cbegin(), lhs.testarrayoftables.cend(), rhs.testarrayoftables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && ((lhs.enemy == rhs.enemy) || (lhs.enemy && rhs.enemy && *lhs.enemy == *rhs.enemy)) && (lhs.testnestedflatbuffer == rhs.testnestedflatbuffer) && ((lhs.testempty == rhs.testempty) || (lhs.testempty && rhs.testempty && *lhs.testempty == *rhs.testempty)) && @@ -2751,10 +2751,10 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_longs == rhs.vector_of_longs) && (lhs.vector_of_doubles == rhs.vector_of_doubles) && ((lhs.parent_namespace_test == rhs.parent_namespace_test) || (lhs.parent_namespace_test && rhs.parent_namespace_test && *lhs.parent_namespace_test == *rhs.parent_namespace_test)) && - (lhs.vector_of_referrables == rhs.vector_of_referrables) && + (lhs.vector_of_referrables.size() == rhs.vector_of_referrables.size() && std::equal(lhs.vector_of_referrables.cbegin(), lhs.vector_of_referrables.cend(), rhs.vector_of_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.single_weak_reference == rhs.single_weak_reference) && (lhs.vector_of_weak_references == rhs.vector_of_weak_references) && - (lhs.vector_of_strong_referrables == rhs.vector_of_strong_referrables) && + (lhs.vector_of_strong_referrables.size() == rhs.vector_of_strong_referrables.size() && std::equal(lhs.vector_of_strong_referrables.cbegin(), lhs.vector_of_strong_referrables.cend(), rhs.vector_of_strong_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.co_owning_reference == rhs.co_owning_reference) && (lhs.vector_of_co_owning_references == rhs.vector_of_co_owning_references) && (lhs.non_owning_reference == rhs.non_owning_reference) && @@ -2764,7 +2764,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_enums == rhs.vector_of_enums) && (lhs.signed_enum == rhs.signed_enum) && (lhs.testrequirednestedflatbuffer == rhs.testrequirednestedflatbuffer) && - (lhs.scalar_key_sorted_tables == rhs.scalar_key_sorted_tables) && + (lhs.scalar_key_sorted_tables.size() == rhs.scalar_key_sorted_tables.size() && std::equal(lhs.scalar_key_sorted_tables.cbegin(), lhs.scalar_key_sorted_tables.cend(), rhs.scalar_key_sorted_tables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.native_inline == rhs.native_inline) && (lhs.long_enum_non_enum_default == rhs.long_enum_non_enum_default) && (lhs.long_enum_normal_default == rhs.long_enum_normal_default); 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 5e2308c7b..899567fa5 100644 --- a/tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h +++ b/tests/monster_test_suffix/filesuffix_only/monster_test_suffix.h @@ -2727,7 +2727,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.test == rhs.test) && (lhs.test4 == rhs.test4) && (lhs.testarrayofstring == rhs.testarrayofstring) && - (lhs.testarrayoftables == rhs.testarrayoftables) && + (lhs.testarrayoftables.size() == rhs.testarrayoftables.size() && std::equal(lhs.testarrayoftables.cbegin(), lhs.testarrayoftables.cend(), rhs.testarrayoftables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && ((lhs.enemy == rhs.enemy) || (lhs.enemy && rhs.enemy && *lhs.enemy == *rhs.enemy)) && (lhs.testnestedflatbuffer == rhs.testnestedflatbuffer) && ((lhs.testempty == rhs.testempty) || (lhs.testempty && rhs.testempty && *lhs.testempty == *rhs.testempty)) && @@ -2751,10 +2751,10 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_longs == rhs.vector_of_longs) && (lhs.vector_of_doubles == rhs.vector_of_doubles) && ((lhs.parent_namespace_test == rhs.parent_namespace_test) || (lhs.parent_namespace_test && rhs.parent_namespace_test && *lhs.parent_namespace_test == *rhs.parent_namespace_test)) && - (lhs.vector_of_referrables == rhs.vector_of_referrables) && + (lhs.vector_of_referrables.size() == rhs.vector_of_referrables.size() && std::equal(lhs.vector_of_referrables.cbegin(), lhs.vector_of_referrables.cend(), rhs.vector_of_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.single_weak_reference == rhs.single_weak_reference) && (lhs.vector_of_weak_references == rhs.vector_of_weak_references) && - (lhs.vector_of_strong_referrables == rhs.vector_of_strong_referrables) && + (lhs.vector_of_strong_referrables.size() == rhs.vector_of_strong_referrables.size() && std::equal(lhs.vector_of_strong_referrables.cbegin(), lhs.vector_of_strong_referrables.cend(), rhs.vector_of_strong_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.co_owning_reference == rhs.co_owning_reference) && (lhs.vector_of_co_owning_references == rhs.vector_of_co_owning_references) && (lhs.non_owning_reference == rhs.non_owning_reference) && @@ -2764,7 +2764,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_enums == rhs.vector_of_enums) && (lhs.signed_enum == rhs.signed_enum) && (lhs.testrequirednestedflatbuffer == rhs.testrequirednestedflatbuffer) && - (lhs.scalar_key_sorted_tables == rhs.scalar_key_sorted_tables) && + (lhs.scalar_key_sorted_tables.size() == rhs.scalar_key_sorted_tables.size() && std::equal(lhs.scalar_key_sorted_tables.cbegin(), lhs.scalar_key_sorted_tables.cend(), rhs.scalar_key_sorted_tables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.native_inline == rhs.native_inline) && (lhs.long_enum_non_enum_default == rhs.long_enum_non_enum_default) && (lhs.long_enum_normal_default == rhs.long_enum_normal_default); diff --git a/tests/monster_test_suffix/monster_test_suffix.hpp b/tests/monster_test_suffix/monster_test_suffix.hpp index 5e2308c7b..899567fa5 100644 --- a/tests/monster_test_suffix/monster_test_suffix.hpp +++ b/tests/monster_test_suffix/monster_test_suffix.hpp @@ -2727,7 +2727,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.test == rhs.test) && (lhs.test4 == rhs.test4) && (lhs.testarrayofstring == rhs.testarrayofstring) && - (lhs.testarrayoftables == rhs.testarrayoftables) && + (lhs.testarrayoftables.size() == rhs.testarrayoftables.size() && std::equal(lhs.testarrayoftables.cbegin(), lhs.testarrayoftables.cend(), rhs.testarrayoftables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && ((lhs.enemy == rhs.enemy) || (lhs.enemy && rhs.enemy && *lhs.enemy == *rhs.enemy)) && (lhs.testnestedflatbuffer == rhs.testnestedflatbuffer) && ((lhs.testempty == rhs.testempty) || (lhs.testempty && rhs.testempty && *lhs.testempty == *rhs.testempty)) && @@ -2751,10 +2751,10 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_longs == rhs.vector_of_longs) && (lhs.vector_of_doubles == rhs.vector_of_doubles) && ((lhs.parent_namespace_test == rhs.parent_namespace_test) || (lhs.parent_namespace_test && rhs.parent_namespace_test && *lhs.parent_namespace_test == *rhs.parent_namespace_test)) && - (lhs.vector_of_referrables == rhs.vector_of_referrables) && + (lhs.vector_of_referrables.size() == rhs.vector_of_referrables.size() && std::equal(lhs.vector_of_referrables.cbegin(), lhs.vector_of_referrables.cend(), rhs.vector_of_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.single_weak_reference == rhs.single_weak_reference) && (lhs.vector_of_weak_references == rhs.vector_of_weak_references) && - (lhs.vector_of_strong_referrables == rhs.vector_of_strong_referrables) && + (lhs.vector_of_strong_referrables.size() == rhs.vector_of_strong_referrables.size() && std::equal(lhs.vector_of_strong_referrables.cbegin(), lhs.vector_of_strong_referrables.cend(), rhs.vector_of_strong_referrables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.co_owning_reference == rhs.co_owning_reference) && (lhs.vector_of_co_owning_references == rhs.vector_of_co_owning_references) && (lhs.non_owning_reference == rhs.non_owning_reference) && @@ -2764,7 +2764,7 @@ inline bool operator==(const MonsterT &lhs, const MonsterT &rhs) { (lhs.vector_of_enums == rhs.vector_of_enums) && (lhs.signed_enum == rhs.signed_enum) && (lhs.testrequirednestedflatbuffer == rhs.testrequirednestedflatbuffer) && - (lhs.scalar_key_sorted_tables == rhs.scalar_key_sorted_tables) && + (lhs.scalar_key_sorted_tables.size() == rhs.scalar_key_sorted_tables.size() && std::equal(lhs.scalar_key_sorted_tables.cbegin(), lhs.scalar_key_sorted_tables.cend(), rhs.scalar_key_sorted_tables.cbegin(), [](flatbuffers::unique_ptr const &a, flatbuffers::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })) && (lhs.native_inline == rhs.native_inline) && (lhs.long_enum_non_enum_default == rhs.long_enum_non_enum_default) && (lhs.long_enum_normal_default == rhs.long_enum_normal_default); diff --git a/tests/test.cpp b/tests/test.cpp index 4d1a9deb3..53eb7839e 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -16,12 +16,14 @@ #include #include +#include #include #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" #include "flatbuffers/minireflect.h" #include "flatbuffers/registry.h" +#include "flatbuffers/stl_emulation.h" #include "flatbuffers/util.h" #include "monster_test_generated.h" #include "namespace_test/namespace_test1_generated.h" @@ -3542,6 +3544,48 @@ void EqualOperatorTest() { b.test.type = Any_Monster; TEST_EQ(b == a, false); TEST_EQ(b != a, true); + + // Test that vector of tables are compared by value and not by reference. + { + // Two tables are equal by default. + MonsterT a, b; + TEST_EQ(a == b, true); + + // Adding only a table to one of the monster vectors should make it not + // equal (due to size mistmatch). + a.testarrayoftables.push_back( + flatbuffers::unique_ptr(new MonsterT)); + TEST_EQ(a == b, false); + + // Adding an equalivant table to the other monster vector should make it + // equal again. + b.testarrayoftables.push_back( + flatbuffers::unique_ptr(new MonsterT)); + TEST_EQ(a == b, true); + + // Create two new monsters that are different. + auto c = flatbuffers::unique_ptr(new MonsterT); + auto d = flatbuffers::unique_ptr(new MonsterT); + c->hp = 1; + d->hp = 2; + TEST_EQ(c == d, false); + + // Adding them to the original monsters should also make them different. + a.testarrayoftables.push_back(std::move(c)); + b.testarrayoftables.push_back(std::move(d)); + TEST_EQ(a == b, false); + + // Remove the mismatching monsters to get back to equality + a.testarrayoftables.pop_back(); + b.testarrayoftables.pop_back(); + TEST_EQ(a == b, true); + + // Check that nullptr are OK. + a.testarrayoftables.push_back(nullptr); + b.testarrayoftables.push_back( + flatbuffers::unique_ptr(new MonsterT)); + TEST_EQ(a == b, false); + } } // For testing any binaries, e.g. from fuzzing. @@ -4443,9 +4487,9 @@ void PrivateAnnotationsLeaks() { // (private) (table), (public) (struct/enum) schemas.push_back( - "table Monster (private) { mana: int; }" - "struct ABC { mana: int; }" - "enum Race:byte { None = -1, Human = 0, }"); + "table Monster (private) { mana: int; }" + "struct ABC { mana: int; }" + "enum Race:byte { None = -1, Human = 0, }"); // (public) (struct) containing (public) (enum) schemas.push_back(