[C++] Vector of Tables equality (#7415)

* Vector of Tables equality

* support nullptr and fix for not being able to use auto in lambda

* use different std::equal overload

* use flatbuffers::unique_ptr

* go back to auto and clang-format fix
This commit is contained in:
Derek Bailey
2022-08-08 21:22:57 -07:00
committed by GitHub
parent a89c279ed6
commit 966362e074
7 changed files with 89 additions and 23 deletions

View File

@@ -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<MyGame::Sample::WeaponT> const &a, flatbuffers::unique_ptr<MyGame::Sample::WeaponT> const &b) { return (a == b) || (a && b && *a == *b); })) &&
(lhs.equipped == rhs.equipped) &&
(lhs.path == rhs.path);
}

View File

@@ -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 + ")";

View File

@@ -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<MyGame::Example::MonsterT> const &a, flatbuffers::unique_ptr<MyGame::Example::MonsterT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::StatT> const &a, flatbuffers::unique_ptr<MyGame::Example::StatT> 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);

View File

@@ -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<MyGame::Example::MonsterT> const &a, flatbuffers::unique_ptr<MyGame::Example::MonsterT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::StatT> const &a, flatbuffers::unique_ptr<MyGame::Example::StatT> 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);

View File

@@ -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<MyGame::Example::MonsterT> const &a, flatbuffers::unique_ptr<MyGame::Example::MonsterT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::StatT> const &a, flatbuffers::unique_ptr<MyGame::Example::StatT> 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);

View File

@@ -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<MyGame::Example::MonsterT> const &a, flatbuffers::unique_ptr<MyGame::Example::MonsterT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::ReferrableT> const &a, flatbuffers::unique_ptr<MyGame::Example::ReferrableT> 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<MyGame::Example::StatT> const &a, flatbuffers::unique_ptr<MyGame::Example::StatT> 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);

View File

@@ -16,12 +16,14 @@
#include <stdint.h>
#include <cmath>
#include <memory>
#include <string>
#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<MonsterT>(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<MonsterT>(new MonsterT));
TEST_EQ(a == b, true);
// Create two new monsters that are different.
auto c = flatbuffers::unique_ptr<MonsterT>(new MonsterT);
auto d = flatbuffers::unique_ptr<MonsterT>(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<MonsterT>(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(