Browse Source

Extract `VariantProxy` from `ElementProxy` and `MemberProxy`

Benoit Blanchon 3 years ago
parent
commit
cf4436e581

+ 1 - 0
CHANGELOG.md

@@ -9,6 +9,7 @@ HEAD
 * Fix comparison operators for `JsonArray`, `JsonArrayConst`, `JsonObject`, and `JsonObjectConst`
 * Fix lax parsing of `true`, `false`, and `null` (issue #1781)
 * Remove undocumented `accept()` functions
+* Remove undocumented `ElementProxy` and `MemberProxy` classes
 * Rename `addElement()` to `add()`
 * Remove `getElement()`, `getOrAddElement()`, `getMember()`, and `getOrAddMember()`
 * Remove `JsonDocument::data()` and `JsonDocument::memoryPool()`

+ 11 - 9
extras/tests/JsonDocument/ElementProxy.cpp

@@ -7,10 +7,12 @@
 
 using namespace ARDUINOJSON_NAMESPACE;
 
+typedef VariantProxy<ElementDataSource<JsonDocument&> > ElementProxy;
+
 TEST_CASE("ElementProxy::add()") {
   DynamicJsonDocument doc(4096);
   doc.add();
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   SECTION("add(int)") {
     ep.add(42);
@@ -36,7 +38,7 @@ TEST_CASE("ElementProxy::add()") {
 TEST_CASE("ElementProxy::clear()") {
   DynamicJsonDocument doc(4096);
   doc.add();
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   SECTION("size goes back to zero") {
     ep.add(42);
@@ -96,7 +98,7 @@ TEST_CASE("ElementProxy::operator==()") {
 TEST_CASE("ElementProxy::remove()") {
   DynamicJsonDocument doc(4096);
   doc.add();
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   SECTION("remove(int)") {
     ep.add(1);
@@ -143,7 +145,7 @@ TEST_CASE("ElementProxy::remove()") {
 
 TEST_CASE("ElementProxy::set()") {
   DynamicJsonDocument doc(4096);
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   SECTION("set(int)") {
     ep.set(42);
@@ -169,7 +171,7 @@ TEST_CASE("ElementProxy::set()") {
 TEST_CASE("ElementProxy::size()") {
   DynamicJsonDocument doc(4096);
   doc.add();
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   SECTION("returns 0") {
     REQUIRE(ep.size() == 0);
@@ -191,7 +193,7 @@ TEST_CASE("ElementProxy::size()") {
 TEST_CASE("ElementProxy::memoryUsage()") {
   DynamicJsonDocument doc(4096);
   doc.add();
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   SECTION("returns 0 for null") {
     REQUIRE(ep.memoryUsage() == 0);
@@ -205,7 +207,7 @@ TEST_CASE("ElementProxy::memoryUsage()") {
 
 TEST_CASE("ElementProxy::operator[]") {
   DynamicJsonDocument doc(4096);
-  ElementProxy<JsonDocument&> ep = doc[1];
+  ElementProxy ep = doc[1];
 
   SECTION("set member") {
     ep["world"] = 42;
@@ -224,7 +226,7 @@ TEST_CASE("ElementProxy cast to JsonVariantConst") {
   DynamicJsonDocument doc(4096);
   doc[0] = "world";
 
-  const ElementProxy<JsonDocument&> ep = doc[0];
+  const ElementProxy ep = doc[0];
 
   JsonVariantConst var = ep;
 
@@ -235,7 +237,7 @@ TEST_CASE("ElementProxy cast to JsonVariant") {
   DynamicJsonDocument doc(4096);
   doc[0] = "world";
 
-  ElementProxy<JsonDocument&> ep = doc[0];
+  ElementProxy ep = doc[0];
 
   JsonVariant var = ep;
 

+ 12 - 10
extras/tests/JsonDocument/MemberProxy.cpp

@@ -7,9 +7,11 @@
 
 using namespace ARDUINOJSON_NAMESPACE;
 
+typedef VariantProxy<MemberDataSource<JsonDocument&, const char*> > MemberProxy;
+
 TEST_CASE("MemberProxy::add()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("add(int)") {
     mp.add(42);
@@ -26,7 +28,7 @@ TEST_CASE("MemberProxy::add()") {
 
 TEST_CASE("MemberProxy::clear()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("size goes back to zero") {
     mp.add(42);
@@ -85,7 +87,7 @@ TEST_CASE("MemberProxy::operator==()") {
 
 TEST_CASE("MemberProxy::containsKey()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("containsKey(const char*)") {
     mp["key"] = "value";
@@ -136,7 +138,7 @@ TEST_CASE("MemberProxy::operator|()") {
 
 TEST_CASE("MemberProxy::remove()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("remove(int)") {
     mp.add(1);
@@ -183,7 +185,7 @@ TEST_CASE("MemberProxy::remove()") {
 
 TEST_CASE("MemberProxy::set()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("set(int)") {
     mp.set(42);
@@ -208,7 +210,7 @@ TEST_CASE("MemberProxy::set()") {
 
 TEST_CASE("MemberProxy::size()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("returns 0") {
     REQUIRE(mp.size() == 0);
@@ -231,7 +233,7 @@ TEST_CASE("MemberProxy::size()") {
 
 TEST_CASE("MemberProxy::memoryUsage()") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("returns 0 when null") {
     REQUIRE(mp.memoryUsage() == 0);
@@ -245,7 +247,7 @@ TEST_CASE("MemberProxy::memoryUsage()") {
 
 TEST_CASE("MemberProxy::operator[]") {
   DynamicJsonDocument doc(4096);
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   SECTION("set member") {
     mp["world"] = 42;
@@ -264,7 +266,7 @@ TEST_CASE("MemberProxy cast to JsonVariantConst") {
   DynamicJsonDocument doc(4096);
   doc["hello"] = "world";
 
-  const MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  const MemberProxy mp = doc["hello"];
 
   JsonVariantConst var = mp;
 
@@ -275,7 +277,7 @@ TEST_CASE("MemberProxy cast to JsonVariant") {
   DynamicJsonDocument doc(4096);
   doc["hello"] = "world";
 
-  MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
+  MemberProxy mp = doc["hello"];
 
   JsonVariant var = mp;
 

+ 5 - 4
extras/tests/Misc/TypeTraits.cpp

@@ -184,12 +184,13 @@ TEST_CASE("Polyfills/type_traits") {
     CHECK((is_convertible<VariantRef, JsonVariantConst>::value == true));
     CHECK((is_convertible<VariantConstRef, JsonVariantConst>::value == true));
     CHECK((is_convertible<ArrayRef, JsonVariantConst>::value == true));
-    CHECK((is_convertible<ElementProxy<ArrayRef>, JsonVariantConst>::value ==
-           true));
+    CHECK((is_convertible<VariantProxy<ElementDataSource<ArrayRef> >,
+                          JsonVariantConst>::value == true));
     CHECK((is_convertible<ArrayConstRef, JsonVariantConst>::value == true));
     CHECK((is_convertible<ObjectRef, JsonVariantConst>::value == true));
-    CHECK((is_convertible<MemberProxy<ObjectRef, const char*>,
-                          JsonVariantConst>::value == true));
+    CHECK(
+        (is_convertible<VariantProxy<MemberDataSource<ObjectRef, const char*> >,
+                        JsonVariantConst>::value == true));
     CHECK((is_convertible<ObjectConstRef, JsonVariantConst>::value == true));
     CHECK(
         (is_convertible<DynamicJsonDocument, JsonVariantConst>::value == true));

+ 4 - 3
src/ArduinoJson/Array/ArrayImpl.hpp

@@ -20,9 +20,10 @@ inline ObjectRef ArrayShortcuts<TArray>::createNestedObject() const {
 }
 
 template <typename TArray>
-inline ElementProxy<TArray> ArrayShortcuts<TArray>::operator[](
-    size_t index) const {
-  return ElementProxy<TArray>(*impl(), index);
+inline VariantProxy<ElementDataSource<TArray> >
+ArrayShortcuts<TArray>::operator[](size_t index) const {
+  return VariantProxy<ElementDataSource<TArray> >(
+      ElementDataSource<TArray>(*impl(), index));
 }
 
 }  // namespace ARDUINOJSON_NAMESPACE

+ 0 - 2
src/ArduinoJson/Array/ArrayRef.hpp

@@ -17,8 +17,6 @@
 namespace ARDUINOJSON_NAMESPACE {
 
 class ObjectRef;
-template <typename>
-class ElementProxy;
 
 template <typename TData>
 class ArrayRefBase {

+ 7 - 2
src/ArduinoJson/Array/ArrayShortcuts.hpp

@@ -11,14 +11,19 @@ namespace ARDUINOJSON_NAMESPACE {
 // Forward declarations.
 class ArrayRef;
 class ObjectRef;
+
+template <typename>
+class ElementDataSource;
+
 template <typename>
-class ElementProxy;
+class VariantProxy;
 
 template <typename TArray>
 class ArrayShortcuts {
  public:
   // Returns the element at specified index if the variant is an array.
-  FORCE_INLINE ElementProxy<TArray> operator[](size_t index) const;
+  FORCE_INLINE VariantProxy<ElementDataSource<TArray> > operator[](
+      size_t index) const;
 
   FORCE_INLINE ObjectRef createNestedObject() const;
 

+ 8 - 157
src/ArduinoJson/Array/ElementProxy.hpp

@@ -4,150 +4,16 @@
 
 #pragma once
 
-#include <ArduinoJson/Configuration.hpp>
-#include <ArduinoJson/Variant/VariantOperators.hpp>
-#include <ArduinoJson/Variant/VariantShortcuts.hpp>
-#include <ArduinoJson/Variant/VariantTo.hpp>
-
-#ifdef _MSC_VER
-#  pragma warning(push)
-#  pragma warning(disable : 4522)
-#endif
+#include <ArduinoJson/Variant/VariantProxy.hpp>
 
 namespace ARDUINOJSON_NAMESPACE {
 
 template <typename TUpstream>
-class ElementProxy  //
-    : public VariantOperators<ElementProxy<TUpstream> >,
-      public VariantShortcuts<ElementProxy<TUpstream> >,
-      public VariantTag {
-  friend class VariantAttorney;
-
+class ElementDataSource {
  public:
-  FORCE_INLINE ElementProxy(TUpstream upstream, size_t index)
+  ElementDataSource(TUpstream upstream, size_t index)
       : _upstream(upstream), _index(index) {}
 
-  FORCE_INLINE ElementProxy(const ElementProxy& src)
-      : _upstream(src._upstream), _index(src._index) {}
-
-  FORCE_INLINE ElementProxy& operator=(const ElementProxy& src) {
-    getOrAddUpstreamVariant().set(src);
-    return *this;
-  }
-
-  template <typename T>
-  FORCE_INLINE ElementProxy& operator=(const T& src) {
-    getOrAddUpstreamVariant().set(src);
-    return *this;
-  }
-
-  template <typename T>
-  FORCE_INLINE ElementProxy& operator=(T* src) {
-    getOrAddUpstreamVariant().set(src);
-    return *this;
-  }
-
-  FORCE_INLINE void clear() const {
-    getUpstreamVariant().clear();
-  }
-
-  FORCE_INLINE bool isNull() const {
-    return getUpstreamVariantConst().isNull();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename enable_if<!is_same<T, char*>::value &&
-                                      !ConverterNeedsWriteableRef<T>::value,
-                                  T>::type
-  as() const {
-    return getUpstreamVariantConst().template as<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename enable_if<ConverterNeedsWriteableRef<T>::value, T>::type
-  as() const {
-    return getUpstreamVariant().template as<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename enable_if<is_same<T, char*>::value, const char*>::type
-  ARDUINOJSON_DEPRECATED("Replace as<char*>() with as<const char*>()")
-      as() const {
-    return as<const char*>();
-  }
-
-  template <typename T>
-  FORCE_INLINE operator T() const {
-    return as<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename VariantTo<T>::type to() {
-    return getOrAddUpstreamVariant().template to<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE
-      typename enable_if<ConverterNeedsWriteableRef<T>::value, bool>::type
-      is() const {
-    return getUpstreamVariant().template is<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE
-      typename enable_if<!ConverterNeedsWriteableRef<T>::value, bool>::type
-      is() const {
-    return getUpstreamVariantConst().template is<T>();
-  }
-
-  FORCE_INLINE void shallowCopy(VariantConstRef value) {
-    getOrAddUpstreamVariant().shallowCopy(value);
-  }
-
-  template <typename T>
-  FORCE_INLINE bool set(const T& value) {
-    return getOrAddUpstreamVariant().set(value);
-  }
-
-  template <typename T>
-  FORCE_INLINE bool set(T* value) {
-    return getOrAddUpstreamVariant().set(value);
-  }
-
-  FORCE_INLINE size_t size() const {
-    return getUpstreamVariantConst().size();
-  }
-
-  FORCE_INLINE size_t memoryUsage() const {
-    return getUpstreamVariantConst().memoryUsage();
-  }
-
-  FORCE_INLINE VariantRef add() const {
-    return getOrAddUpstreamVariant().add();
-  }
-
-  using ArrayShortcuts<ElementProxy<TUpstream> >::add;
-
-  FORCE_INLINE void remove(size_t index) const {
-    getUpstreamVariant().remove(index);
-  }
-  // remove(char*) const
-  // remove(const char*) const
-  // remove(const __FlashStringHelper*) const
-  template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value>::type remove(
-      TChar* key) const {
-    getUpstreamVariant().remove(key);
-  }
-  // remove(const std::string&) const
-  // remove(const String&) const
-  template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value>::type remove(
-      const TString& key) const {
-    getUpstreamVariant().remove(key);
-  }
-
- protected:
   FORCE_INLINE MemoryPool* getPool() const {
     return VariantAttorney::getPool(_upstream);
   }
@@ -162,28 +28,13 @@ class ElementProxy  //
   }
 
  private:
-  FORCE_INLINE VariantRef getUpstreamVariant() const {
-    return VariantRef(getPool(), getData());
-  }
-
-  FORCE_INLINE VariantConstRef getUpstreamVariantConst() const {
-    return VariantConstRef(getData());
-  }
-
-  FORCE_INLINE VariantRef getOrAddUpstreamVariant() const {
-    return VariantRef(getPool(), getOrCreateData());
-  }
-
-  friend void convertToJson(const ElementProxy& src, VariantRef dst) {
-    dst.set(src.getUpstreamVariantConst());
-  }
+#if defined _MSC_VER && _MSC_VER <= 1800  // Visual Studio 2013 or below
+  // Prevent "assignment operator could not be generated"
+  ElementDataSource& operator=(const ElementDataSource&);
+#endif
 
   TUpstream _upstream;
-  const size_t _index;
+  size_t _index;
 };
 
 }  // namespace ARDUINOJSON_NAMESPACE
-
-#ifdef _MSC_VER
-#  pragma warning(pop)
-#endif

+ 3 - 9
src/ArduinoJson/Deserialization/Readers/VariantReader.hpp

@@ -9,15 +9,9 @@
 
 namespace ARDUINOJSON_NAMESPACE {
 
-template <typename TArray>
-struct Reader<ElementProxy<TArray>, void> : Reader<char*, void> {
-  explicit Reader(const ElementProxy<TArray>& x)
-      : Reader<char*, void>(x.template as<const char*>()) {}
-};
-
-template <typename TObject, typename TStringRef>
-struct Reader<MemberProxy<TObject, TStringRef>, void> : Reader<char*, void> {
-  explicit Reader(const MemberProxy<TObject, TStringRef>& x)
+template <typename TSource>
+struct Reader<VariantProxy<TSource>, void> : Reader<char*, void> {
+  explicit Reader(const VariantProxy<TSource>& x)
       : Reader<char*, void>(x.template as<const char*>()) {}
 };
 

+ 14 - 8
src/ArduinoJson/Document/JsonDocument.hpp

@@ -139,20 +139,24 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   // operator[](const std::string&)
   // operator[](const String&)
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value,
-                                  MemberProxy<JsonDocument&, TString> >::type
+  FORCE_INLINE typename enable_if<
+      IsString<TString>::value,
+      VariantProxy<MemberDataSource<JsonDocument&, TString> > >::type
   operator[](const TString& key) {
-    return MemberProxy<JsonDocument&, TString>(*this, key);
+    return VariantProxy<MemberDataSource<JsonDocument&, TString> >(
+        MemberDataSource<JsonDocument&, TString>(*this, key));
   }
 
   // operator[](char*)
   // operator[](const char*)
   // operator[](const __FlashStringHelper*)
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value,
-                                  MemberProxy<JsonDocument&, TChar*> >::type
+  FORCE_INLINE typename enable_if<
+      IsString<TChar*>::value,
+      VariantProxy<MemberDataSource<JsonDocument&, TChar*> > >::type
   operator[](TChar* key) {
-    return MemberProxy<JsonDocument&, TChar*>(*this, key);
+    return VariantProxy<MemberDataSource<JsonDocument&, TChar*> >(
+        MemberDataSource<JsonDocument&, TChar*>(*this, key));
   }
 
   // operator[](const std::string&) const
@@ -174,8 +178,10 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
     return VariantConstRef(_data.getMember(adaptString(key)));
   }
 
-  FORCE_INLINE ElementProxy<JsonDocument&> operator[](size_t index) {
-    return ElementProxy<JsonDocument&>(*this, index);
+  FORCE_INLINE VariantProxy<ElementDataSource<JsonDocument&> > operator[](
+      size_t index) {
+    return VariantProxy<ElementDataSource<JsonDocument&> >(
+        ElementDataSource<JsonDocument&>(*this, index));
   }
 
   FORCE_INLINE VariantConstRef operator[](size_t index) const {

+ 7 - 159
src/ArduinoJson/Object/MemberProxy.hpp

@@ -4,153 +4,16 @@
 
 #pragma once
 
-#include <ArduinoJson/Configuration.hpp>
-#include <ArduinoJson/Polyfills/type_traits.hpp>
-#include <ArduinoJson/Variant/Converter.hpp>
-#include <ArduinoJson/Variant/VariantOperators.hpp>
-#include <ArduinoJson/Variant/VariantRef.hpp>
-#include <ArduinoJson/Variant/VariantShortcuts.hpp>
-#include <ArduinoJson/Variant/VariantTo.hpp>
-
-#ifdef _MSC_VER
-#  pragma warning(push)
-#  pragma warning(disable : 4522)
-#endif
+#include <ArduinoJson/Variant/VariantProxy.hpp>
 
 namespace ARDUINOJSON_NAMESPACE {
 
 template <typename TUpstream, typename TStringRef>
-class MemberProxy  //
-    : public VariantOperators<MemberProxy<TUpstream, TStringRef> >,
-      public VariantShortcuts<MemberProxy<TUpstream, TStringRef> >,
-      public VariantTag {
-  friend class VariantAttorney;
-
+class MemberDataSource {
  public:
-  FORCE_INLINE MemberProxy(TUpstream upstream, TStringRef key)
+  FORCE_INLINE MemberDataSource(TUpstream upstream, TStringRef key)
       : _upstream(upstream), _key(key) {}
 
-  FORCE_INLINE MemberProxy(const MemberProxy& src)
-      : _upstream(src._upstream), _key(src._key) {}
-
-  FORCE_INLINE MemberProxy& operator=(const MemberProxy& src) {
-    getOrAddUpstreamVariant().set(src);
-    return *this;
-  }
-
-  template <typename T>
-  FORCE_INLINE MemberProxy& operator=(const T& src) {
-    getOrAddUpstreamVariant().set(src);
-    return *this;
-  }
-
-  template <typename T>
-  FORCE_INLINE MemberProxy& operator=(T* src) {
-    getOrAddUpstreamVariant().set(src);
-    return *this;
-  }
-
-  FORCE_INLINE void clear() const {
-    getUpstreamVariant().clear();
-  }
-
-  FORCE_INLINE bool isNull() const {
-    return getUpstreamVariantConst().isNull();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename enable_if<!is_same<T, char*>::value &&
-                                      !ConverterNeedsWriteableRef<T>::value,
-                                  T>::type
-  as() const {
-    return getUpstreamVariantConst().template as<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename enable_if<ConverterNeedsWriteableRef<T>::value, T>::type
-  as() const {
-    return getUpstreamVariant().template as<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename enable_if<is_same<T, char*>::value, const char*>::type
-  ARDUINOJSON_DEPRECATED("Replace as<char*>() with as<const char*>()")
-      as() const {
-    return as<const char*>();
-  }
-
-  template <typename T>
-  FORCE_INLINE operator T() const {
-    return as<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE typename VariantTo<T>::type to() {
-    return getOrAddUpstreamVariant().template to<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE
-      typename enable_if<ConverterNeedsWriteableRef<T>::value, bool>::type
-      is() const {
-    return getUpstreamVariant().template is<T>();
-  }
-
-  template <typename T>
-  FORCE_INLINE
-      typename enable_if<!ConverterNeedsWriteableRef<T>::value, bool>::type
-      is() const {
-    return getUpstreamVariantConst().template is<T>();
-  }
-
-  FORCE_INLINE void shallowCopy(VariantConstRef value) {
-    getOrAddUpstreamVariant().shallowCopy(value);
-  }
-
-  template <typename T>
-  FORCE_INLINE bool set(const T& value) {
-    return getOrAddUpstreamVariant().set(value);
-  }
-
-  template <typename T>
-  FORCE_INLINE bool set(T* value) {
-    return getOrAddUpstreamVariant().set(value);
-  }
-
-  FORCE_INLINE size_t size() const {
-    return getUpstreamVariantConst().size();
-  }
-
-  FORCE_INLINE size_t memoryUsage() const {
-    return getUpstreamVariantConst().memoryUsage();
-  }
-
-  FORCE_INLINE VariantRef add() const {
-    return getOrAddUpstreamVariant().add();
-  }
-
-  using ArrayShortcuts<MemberProxy<TUpstream, TStringRef> >::add;
-
-  FORCE_INLINE void remove(size_t index) const {
-    getUpstreamVariant().remove(index);
-  }
-  // remove(char*) const
-  // remove(const char*) const
-  // remove(const __FlashStringHelper*) const
-  template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value>::type remove(
-      TChar* key) const {
-    getUpstreamVariant().remove(key);
-  }
-  // remove(const std::string&) const
-  // remove(const String&) const
-  template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value>::type remove(
-      const TString& key) const {
-    getUpstreamVariant().remove(key);
-  }
-
- protected:
   FORCE_INLINE MemoryPool* getPool() const {
     return VariantAttorney::getPool(_upstream);
   }
@@ -166,28 +29,13 @@ class MemberProxy  //
   }
 
  private:
-  FORCE_INLINE VariantRef getUpstreamVariant() const {
-    return VariantRef(getPool(), getData());
-  }
-
-  FORCE_INLINE VariantConstRef getUpstreamVariantConst() const {
-    return VariantConstRef(getData());
-  }
-
-  FORCE_INLINE VariantRef getOrAddUpstreamVariant() const {
-    return VariantRef(getPool(), getOrCreateData());
-  }
-
-  friend void convertToJson(const MemberProxy& src, VariantRef dst) {
-    dst.set(src.getUpstreamVariantConst());
-  }
+#if defined _MSC_VER && _MSC_VER <= 1800  // Visual Studio 2013 or below
+  // Prevent "assignment operator could not be generated"
+  MemberDataSource& operator=(const MemberDataSource&);
+#endif
 
   TUpstream _upstream;
   TStringRef _key;
 };
 
 }  // namespace ARDUINOJSON_NAMESPACE
-
-#ifdef _MSC_VER
-#  pragma warning(pop)
-#endif

+ 11 - 7
src/ArduinoJson/Object/ObjectImpl.hpp

@@ -54,18 +54,22 @@ ObjectShortcuts<TObject>::containsKey(TChar* key) const {
 
 template <typename TObject>
 template <typename TString>
-inline typename enable_if<IsString<TString*>::value,
-                          MemberProxy<TObject, TString*> >::type
+inline typename enable_if<
+    IsString<TString*>::value,
+    VariantProxy<MemberDataSource<TObject, TString*> > >::type
 ObjectShortcuts<TObject>::operator[](TString* key) const {
-  return MemberProxy<TObject, TString*>(*impl(), key);
+  return VariantProxy<MemberDataSource<TObject, TString*> >(
+      MemberDataSource<TObject, TString*>(*impl(), key));
 }
 
 template <typename TObject>
 template <typename TString>
-inline typename enable_if<IsString<TString>::value,
-                          MemberProxy<TObject, TString> >::type
-ObjectShortcuts<TObject>::operator[](const TString& key) const {
-  return MemberProxy<TObject, TString>(*impl(), key);
+inline
+    typename enable_if<IsString<TString>::value,
+                       VariantProxy<MemberDataSource<TObject, TString> > >::type
+    ObjectShortcuts<TObject>::operator[](const TString& key) const {
+  return VariantProxy<MemberDataSource<TObject, TString> >(
+      MemberDataSource<TObject, TString>(*impl(), key));
 }
 
 }  // namespace ARDUINOJSON_NAMESPACE

+ 10 - 5
src/ArduinoJson/Object/ObjectShortcuts.hpp

@@ -9,8 +9,11 @@
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 
 namespace ARDUINOJSON_NAMESPACE {
+template <typename TSource>
+class VariantProxy;
+
 template <typename TParent, typename TStringRef>
-class MemberProxy;
+class MemberDataSource;
 
 template <typename TObject>
 class ObjectShortcuts {
@@ -31,16 +34,18 @@ class ObjectShortcuts {
   // operator[](const std::string&) const
   // operator[](const String&) const
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value,
-                                  MemberProxy<TObject, TString> >::type
+  FORCE_INLINE typename enable_if<
+      IsString<TString>::value,
+      VariantProxy<MemberDataSource<TObject, TString> > >::type
   operator[](const TString& key) const;
 
   // operator[](char*) const
   // operator[](const char*) const
   // operator[](const __FlashStringHelper*) const
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value,
-                                  MemberProxy<TObject, TChar*> >::type
+  FORCE_INLINE typename enable_if<
+      IsString<TChar*>::value,
+      VariantProxy<MemberDataSource<TObject, TChar*> > >::type
   operator[](TChar* key) const;
 
   // createNestedArray(const std::string&) const

+ 185 - 0
src/ArduinoJson/Variant/VariantProxy.hpp

@@ -0,0 +1,185 @@
+// ArduinoJson - https://arduinojson.org
+// Copyright © 2014-2022, Benoit BLANCHON
+// MIT License
+
+#pragma once
+
+#include <ArduinoJson/Configuration.hpp>
+#include <ArduinoJson/Variant/VariantOperators.hpp>
+#include <ArduinoJson/Variant/VariantShortcuts.hpp>
+#include <ArduinoJson/Variant/VariantTo.hpp>
+
+#ifdef _MSC_VER
+#  pragma warning(push)
+#  pragma warning(disable : 4522)
+#endif
+
+namespace ARDUINOJSON_NAMESPACE {
+
+template <typename TDataSource>
+class VariantProxy : public VariantShortcuts<VariantProxy<TDataSource> >,
+                     public VariantOperators<VariantProxy<TDataSource> >,
+                     public VariantTag {
+  friend class VariantAttorney;
+
+ public:
+  explicit FORCE_INLINE VariantProxy(TDataSource source) : _source(source) {}
+
+  // Copy-constructor required because of user-defined copy-assignment operator
+  FORCE_INLINE VariantProxy(const VariantProxy& src) : _source(src._source) {}
+
+  FORCE_INLINE VariantProxy& operator=(const VariantProxy& src) {
+    getOrAddUpstreamVariant().set(src);
+    return *this;
+  }
+
+  template <typename T>
+  FORCE_INLINE VariantProxy& operator=(const T& src) {
+    getOrAddUpstreamVariant().set(src);
+    return *this;
+  }
+
+  template <typename T>
+  FORCE_INLINE VariantProxy& operator=(T* src) {
+    getOrAddUpstreamVariant().set(src);
+    return *this;
+  }
+
+  FORCE_INLINE void clear() const {
+    getUpstreamVariant().clear();
+  }
+
+  FORCE_INLINE bool isNull() const {
+    return getUpstreamVariantConst().isNull();
+  }
+
+  template <typename T>
+  FORCE_INLINE typename enable_if<!is_same<T, char*>::value &&
+                                      !ConverterNeedsWriteableRef<T>::value,
+                                  T>::type
+  as() const {
+    return getUpstreamVariantConst().template as<T>();
+  }
+
+  template <typename T>
+  FORCE_INLINE typename enable_if<ConverterNeedsWriteableRef<T>::value, T>::type
+  as() const {
+    return getUpstreamVariant().template as<T>();
+  }
+
+  template <typename T>
+  FORCE_INLINE typename enable_if<is_same<T, char*>::value, const char*>::type
+  ARDUINOJSON_DEPRECATED("Replace as<char*>() with as<const char*>()")
+      as() const {
+    return as<const char*>();
+  }
+
+  template <typename T>
+  FORCE_INLINE operator T() const {
+    return as<T>();
+  }
+
+  template <typename T>
+  FORCE_INLINE typename VariantTo<T>::type to() {
+    return getOrAddUpstreamVariant().template to<T>();
+  }
+
+  template <typename T>
+  FORCE_INLINE
+      typename enable_if<ConverterNeedsWriteableRef<T>::value, bool>::type
+      is() const {
+    return getUpstreamVariant().template is<T>();
+  }
+
+  template <typename T>
+  FORCE_INLINE
+      typename enable_if<!ConverterNeedsWriteableRef<T>::value, bool>::type
+      is() const {
+    return getUpstreamVariantConst().template is<T>();
+  }
+
+  FORCE_INLINE void shallowCopy(VariantConstRef value) {
+    getOrAddUpstreamVariant().shallowCopy(value);
+  }
+
+  template <typename T>
+  FORCE_INLINE bool set(const T& value) {
+    return getOrAddUpstreamVariant().set(value);
+  }
+
+  template <typename T>
+  FORCE_INLINE bool set(T* value) {
+    return getOrAddUpstreamVariant().set(value);
+  }
+
+  FORCE_INLINE size_t size() const {
+    return getUpstreamVariantConst().size();
+  }
+
+  FORCE_INLINE size_t memoryUsage() const {
+    return getUpstreamVariantConst().memoryUsage();
+  }
+
+  FORCE_INLINE VariantRef add() const {
+    return getOrAddUpstreamVariant().add();
+  }
+
+  using ArrayShortcuts<VariantProxy<TDataSource> >::add;
+
+  FORCE_INLINE void remove(size_t index) const {
+    getUpstreamVariant().remove(index);
+  }
+  // remove(char*) const
+  // remove(const char*) const
+  // remove(const __FlashStringHelper*) const
+  template <typename TChar>
+  FORCE_INLINE typename enable_if<IsString<TChar*>::value>::type remove(
+      TChar* key) const {
+    getUpstreamVariant().remove(key);
+  }
+  // remove(const std::string&) const
+  // remove(const String&) const
+  template <typename TString>
+  FORCE_INLINE typename enable_if<IsString<TString>::value>::type remove(
+      const TString& key) const {
+    getUpstreamVariant().remove(key);
+  }
+
+ protected:
+  FORCE_INLINE MemoryPool* getPool() const {
+    return _source.getPool();
+  }
+
+  FORCE_INLINE VariantData* getData() const {
+    return _source.getData();
+  }
+
+  FORCE_INLINE VariantData* getOrCreateData() const {
+    return _source.getOrCreateData();
+  }
+
+ private:
+  FORCE_INLINE VariantRef getUpstreamVariant() const {
+    return VariantRef(getPool(), getData());
+  }
+
+  FORCE_INLINE VariantConstRef getUpstreamVariantConst() const {
+    return VariantConstRef(getData());
+  }
+
+  FORCE_INLINE VariantRef getOrAddUpstreamVariant() const {
+    return VariantRef(getPool(), getOrCreateData());
+  }
+
+  friend void convertToJson(const VariantProxy& src, VariantRef dst) {
+    dst.set(src.getUpstreamVariantConst());
+  }
+
+  const TDataSource _source;
+};
+
+}  // namespace ARDUINOJSON_NAMESPACE
+
+#ifdef _MSC_VER
+#  pragma warning(pop)
+#endif