From 95eef1995af6dde37f6797469adf7ea65e726aa8 Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 25 Nov 2025 13:13:21 +0000 Subject: [PATCH] Variant: Make DynamicObject comparison more intuitive --- BREAKING_CHANGES.md | 22 +++++++++ modules/juce_core/containers/juce_Variant.cpp | 49 ++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 8c3351e8d9..6b08357c9d 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -2,6 +2,28 @@ # Version 8.0.11 +## Change + +var::equals(), var::operator==(), and var::operator!=() will now carry out a +deep equality check when comparing two stored DynamicObjects, as opposed to +just comparing the objects' addresses, which was the old behaviour. + +**Possible Issues** + +Program that depend on variants only comparing equal when the object pointers +are equal will now exhibit unexpected behaviour. + +**Workaround** + +There is no workaround for this change. + +**Rationale** + +The previous behaviour was unintuitive, as it meant that two different var +instances may compare unequal, even when those var instances were both created +by parsing the same JSON string. + + ## Change Enabling JUCE_ASIO will now default to using bundled ASIO sources. diff --git a/modules/juce_core/containers/juce_Variant.cpp b/modules/juce_core/containers/juce_Variant.cpp index 0b45553bef..65600d4f0b 100644 --- a/modules/juce_core/containers/juce_Variant.cpp +++ b/modules/juce_core/containers/juce_Variant.cpp @@ -343,7 +343,13 @@ struct var::VariantType static bool objectEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - return otherType.toObject (otherData) == data.objectValue; + const auto* otherObject = otherType.toObject (otherData); + + if (auto* dynamicObjectOther = dynamic_cast (otherObject)) + if (auto* dynamicObjectSelf = dynamic_cast (data.objectValue)) + return dynamicObjectSelf->equals (*dynamicObjectOther); + + return otherObject == data.objectValue; } static void objectWriteToStream (const ValueUnion&, OutputStream& output) @@ -904,4 +910,45 @@ JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct VariantTests : public UnitTest +{ +public: + VariantTests() : UnitTest ("Variant", UnitTestCategories::json) {} + + void runTest() override + { + beginTest ("object comparisons have value semantics"); + { + DynamicObject::Ptr a = new DynamicObject; + a->setProperty ("foo", 1); + a->setProperty ("bar", "hello world"); + a->setProperty ("baz", 2.3); + + const Array nestedArray { var { 5 }, var { 6 }, var { 7 }, var { std::invoke ([] + { + auto* result = new DynamicObject; + result->setProperty ("innerA", 0); + result->setProperty ("innerB", ""); + return result; + }) } }; + + a->setProperty ("nestedArray", nestedArray); + + var varB = a->clone().release(); + var varA = a.get(); + + expect (varA.equals (varB)); + expect (varB.equals (varA)); + } + } +}; + +static VariantTests variantTests; + +#endif + } // namespace juce