Преглед изворни кода

Simplified `JsonVariant::as<T>()` to always return `T`

Benoit Blanchon пре 4 година
родитељ
комит
e4ce75e20c

+ 30 - 1
CHANGELOG.md

@@ -11,9 +11,12 @@ HEAD
 * Added `JsonVariant::is<JsonArrayConst/JsonObjectConst>()` (issue #1412)
 * Added `JsonVariant::is<JsonVariant/JsonVariantConst>()` (issue #1412)
 * Changed `JsonVariantConst::is<JsonArray/JsonObject>()` to return `false` (issue #1412)
+* Simplified `JsonVariant::as<T>()` to always return `T` (see below)
 
 > ### BREAKING CHANGES
 >
+> #### Support for `char` removed
+>
 > We cannot cast a `JsonVariant` to a `char` anymore, so the following will break:
 > ```c++
 > char age = doc["age"];  //  error: no matching function for call to 'variantAs(VariantData*&)'
@@ -34,13 +37,39 @@ HEAD
 > doc["age"] = age;  // OK
 > ```
 >
+> #### `as<T>()` always returns `T`
+>
+> Previously, `JsonVariant::as<T>()` could return a type different from `T`.
+> The most common example is `as<char*>()` that returned a `const char*`.
+> While this feature simplified a few use cases, it was confusing and complicated the
+> implementation of custom converters.
+>
+> Starting from this version, `as<T>` doesn't try to auto-correct the return type and always return `T`,
+> which means that you cannot write this anymore:
+>
+> ```c++
+> Serial.println(doc["sensor"].as<char*>());  // error: invalid conversion from 'const char*' to 'char*' [-fpermissive]
+
+> ```
+> 
+> Instead, you must write:
+>
+> ```c++
+> Serial.println(doc["sensor"].as<const char*>());  // OK
+> ```
+>
+>
+> #### `DeserializationError::NotSupported` removed
+>
 > On a different topic, `DeserializationError::NotSupported` has been removed.
 > Instead of returning this error:
 >
 > * `deserializeJson()` leaves `\uXXXX` unchanged (only when `ARDUINOJSON_DECODE_UNICODE` is `0`)
 > * `deserializeMsgPack()` replaces unsupported values with `null`s
 >
-> Lastly, a very minor change conserns `JsonVariantConst::is<T>()`.
+> #### Const-aware `is<T>()`
+>
+> Lastly, a very minor change concerns `JsonVariantConst::is<T>()`.
 > It used to return `true` for `JsonArray` and `JsonOject`, but now it returns `false`.
 > Instead, you must use `JsonArrayConst` and `JsonObjectConst`.
 

+ 1 - 1
examples/JsonHttpClient/JsonHttpClient.ino

@@ -92,7 +92,7 @@ void setup() {
 
   // Extract values
   Serial.println(F("Response:"));
-  Serial.println(doc["sensor"].as<char*>());
+  Serial.println(doc["sensor"].as<const char*>());
   Serial.println(doc["time"].as<long>());
   Serial.println(doc["data"][0].as<float>(), 6);
   Serial.println(doc["data"][1].as<float>(), 6);

+ 0 - 1
extras/tests/JsonArray/subscript.cpp

@@ -60,7 +60,6 @@ TEST_CASE("JsonArray::operator[]") {
 
     array[0] = str;
     REQUIRE(str == array[0].as<const char*>());
-    REQUIRE(str == array[0].as<char*>());  // <- short hand
     REQUIRE(true == array[0].is<const char*>());
     REQUIRE(false == array[0].is<int>());
   }

+ 2 - 2
extras/tests/JsonDeserializer/array.cpp

@@ -99,8 +99,8 @@ TEST_CASE("deserialize JSON array") {
 
       REQUIRE(err == DeserializationError::Ok);
       REQUIRE(2 == arr.size());
-      REQUIRE(arr[0].as<char*>() == 0);
-      REQUIRE(arr[1].as<char*>() == 0);
+      REQUIRE(arr[0].as<const char*>() == 0);
+      REQUIRE(arr[1].as<const char*>() == 0);
     }
   }
 

+ 2 - 2
extras/tests/JsonDeserializer/object.cpp

@@ -182,8 +182,8 @@ TEST_CASE("deserialize JSON object") {
       REQUIRE(err == DeserializationError::Ok);
       REQUIRE(doc.is<JsonObject>());
       REQUIRE(obj.size() == 2);
-      REQUIRE(obj["key1"].as<char*>() == 0);
-      REQUIRE(obj["key2"].as<char*>() == 0);
+      REQUIRE(obj["key1"].as<const char*>() == 0);
+      REQUIRE(obj["key2"].as<const char*>() == 0);
     }
 
     SECTION("Array") {

+ 2 - 3
extras/tests/JsonObject/subscript.cpp

@@ -48,7 +48,6 @@ TEST_CASE("JsonObject::operator[]") {
     REQUIRE(true == obj["hello"].is<const char*>());
     REQUIRE(false == obj["hello"].is<long>());
     REQUIRE(std::string("h3110") == obj["hello"].as<const char*>());
-    REQUIRE(std::string("h3110") == obj["hello"].as<char*>());  // <- short hand
   }
 
   SECTION("array") {
@@ -189,7 +188,7 @@ TEST_CASE("JsonObject::operator[]") {
 
     obj["hello"] = vla;
 
-    REQUIRE(std::string("world") == obj["hello"].as<char*>());
+    REQUIRE(std::string("world") == obj["hello"].as<const char*>());
   }
 
   SECTION("obj.set(VLA, str)") {
@@ -209,7 +208,7 @@ TEST_CASE("JsonObject::operator[]") {
 
     obj["hello"].set(vla);
 
-    REQUIRE(std::string("world") == obj["hello"].as<char*>());
+    REQUIRE(std::string("world") == obj["hello"].as<const char*>());
   }
 
   SECTION("obj[VLA]") {

+ 4 - 5
extras/tests/JsonVariant/as.cpp

@@ -22,7 +22,7 @@ TEST_CASE("JsonVariant::as()") {
     REQUIRE(false == variant.as<bool>());
     REQUIRE(0 == variant.as<int>());
     REQUIRE(0.0f == variant.as<float>());
-    REQUIRE(0 == variant.as<char*>());
+    REQUIRE(0 == variant.as<const char*>());
     REQUIRE("null" == variant.as<std::string>());
   }
 
@@ -104,7 +104,7 @@ TEST_CASE("JsonVariant::as()") {
     REQUIRE(variant.as<bool>() == true);
     REQUIRE(variant.as<long>() == 0L);
     REQUIRE(variant.as<const char*>() == std::string("hello"));
-    REQUIRE(variant.as<char*>() == std::string("hello"));
+    REQUIRE(variant.as<const char*>() == std::string("hello"));
     REQUIRE(variant.as<std::string>() == std::string("hello"));
   }
 
@@ -114,7 +114,7 @@ TEST_CASE("JsonVariant::as()") {
     REQUIRE(variant.as<bool>() == true);
     REQUIRE(variant.as<long>() == 4L);
     REQUIRE(variant.as<double>() == 4.2);
-    REQUIRE(variant.as<char*>() == std::string("4.2"));
+    REQUIRE(variant.as<const char*>() == std::string("4.2"));
     REQUIRE(variant.as<std::string>() == std::string("4.2"));
   }
 
@@ -211,8 +211,7 @@ TEST_CASE("JsonVariant::as()") {
     REQUIRE(cvar.as<bool>() == true);
     REQUIRE(cvar.as<long>() == 0L);
     REQUIRE(cvar.as<const char*>() == std::string("hello"));
-    REQUIRE(cvar.as<char*>() == std::string("hello"));
-    // REQUIRE(cvar.as<std::string>() == std::string("hello"));
+    REQUIRE(cvar.as<std::string>() == std::string("hello"));
   }
 
   SECTION("as<enum>()") {

+ 2 - 2
extras/tests/JsonVariant/undefined.cpp

@@ -17,8 +17,8 @@ TEST_CASE("JsonVariant undefined") {
       REQUIRE(variant.as<unsigned>() == 0);
     }
 
-    SECTION("char*") {
-      REQUIRE(variant.as<char*>() == 0);
+    SECTION("const char*") {
+      REQUIRE(variant.as<const char*>() == 0);
     }
 
     SECTION("double") {

+ 5 - 5
extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp

@@ -19,7 +19,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
       deserializeJson(doc, "[\"example\",\"example\"]");
 
       CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
-      CHECK(doc[0].as<char*>() != doc[1].as<char*>());
+      CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
     }
 
     SECTION("Deduplicate keys") {
@@ -42,7 +42,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
         doc.add(std::string("example"));
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
-        CHECK(doc[0].as<char*>() != doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
       }
 
       SECTION("char*") {
@@ -51,7 +51,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
         doc.add(value);
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
-        CHECK(doc[0].as<char*>() != doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
       }
 
       SECTION("Arduino String") {
@@ -59,7 +59,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
         doc.add(String("example"));
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
-        CHECK(doc[0].as<char*>() != doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
       }
 
       SECTION("Flash string") {
@@ -67,7 +67,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
         doc.add(F("example"));
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
-        CHECK(doc[0].as<char*>() != doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() != doc[1].as<const char*>());
       }
     }
 

+ 5 - 5
extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp

@@ -19,7 +19,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
       deserializeJson(doc, "[\"example\",\"example\"]");
 
       CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
-      CHECK(doc[0].as<char*>() == doc[1].as<char*>());
+      CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
     }
 
     SECTION("Deduplicate keys") {
@@ -41,7 +41,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
         doc.add(std::string("example"));
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
-        CHECK(doc[0].as<char*>() == doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
       }
 
       SECTION("char*") {
@@ -50,7 +50,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
         doc.add(value);
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
-        CHECK(doc[0].as<char*>() == doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
       }
 
       SECTION("Arduino String") {
@@ -58,7 +58,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
         doc.add(String("example"));
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
-        CHECK(doc[0].as<char*>() == doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
       }
 
       SECTION("Flash string") {
@@ -66,7 +66,7 @@ TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
         doc.add(F("example"));
 
         CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
-        CHECK(doc[0].as<char*>() == doc[1].as<char*>());
+        CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
       }
     }
 

+ 1 - 1
src/ArduinoJson/Array/ElementProxy.hpp

@@ -65,7 +65,7 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
   }
 
   template <typename T>
-  FORCE_INLINE typename VariantAs<T>::type as() const {
+  FORCE_INLINE T as() const {
     return getUpstreamElement().template as<T>();
   }
 

+ 3 - 3
src/ArduinoJson/Document/JsonDocument.hpp

@@ -21,12 +21,12 @@ class JsonDocument : public Visitable {
   }
 
   template <typename T>
-  typename VariantAs<T>::type as() {
+  T as() {
     return getVariant().template as<T>();
   }
 
   template <typename T>
-  typename VariantConstAs<T>::type as() const {
+  T as() const {
     return getVariant().template as<T>();
   }
 
@@ -70,7 +70,7 @@ class JsonDocument : public Visitable {
   }
 
   bool set(const JsonDocument& src) {
-    return to<VariantRef>().set(src.as<VariantRef>());
+    return to<VariantRef>().set(src.as<VariantConstRef>());
   }
 
   template <typename T>

+ 1 - 1
src/ArduinoJson/Object/MemberProxy.hpp

@@ -82,7 +82,7 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
   }
 
   template <typename TValue>
-  FORCE_INLINE typename VariantAs<TValue>::type as() const {
+  FORCE_INLINE TValue as() const {
     return getUpstreamMember().template as<TValue>();
   }
 

+ 2 - 40
src/ArduinoJson/Variant/VariantAs.hpp

@@ -16,42 +16,6 @@ class ObjectConstRef;
 class VariantRef;
 class VariantConstRef;
 
-// A metafunction that returns the type of the value returned by
-// VariantRef::as<T>()
-template <typename T>
-struct VariantAs {
-  typedef T type;
-};
-
-template <>
-struct VariantAs<char*> {
-  typedef const char* type;
-};
-
-// A metafunction that returns the type of the value returned by
-// VariantRef::as<T>()
-template <typename T>
-struct VariantConstAs {
-  typedef typename VariantAs<T>::type type;
-};
-
-template <>
-struct VariantConstAs<VariantRef> {
-  typedef VariantConstRef type;
-};
-
-template <>
-struct VariantConstAs<ObjectRef> {
-  typedef ObjectConstRef type;
-};
-
-template <>
-struct VariantConstAs<ArrayRef> {
-  typedef ArrayConstRef type;
-};
-
-// ---
-
 template <typename T>
 inline typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
                               !is_same<char, T>::value,
@@ -80,10 +44,8 @@ inline typename enable_if<is_floating_point<T>::value, T>::type variantAs(
 }
 
 template <typename T>
-inline typename enable_if<is_same<T, const char*>::value ||
-                              is_same<T, char*>::value,
-                          const char*>::type
-variantAs(const VariantData* data) {
+inline typename enable_if<is_same<T, const char*>::value, T>::type variantAs(
+    const VariantData* data) {
   return data != 0 ? data->asString() : 0;
 }
 

+ 12 - 5
src/ArduinoJson/Variant/VariantOperators.hpp

@@ -23,18 +23,25 @@ struct VariantOperators {
   // int operator|(JsonVariant, int)
   // float operator|(JsonVariant, float)
   // bool operator|(JsonVariant, bool)
-  // const char* operator|(JsonVariant, const char*)
-  // char* operator|(JsonVariant, const char*)
   template <typename T>
-  friend typename enable_if<!IsVariant<T>::value,
-                            typename VariantAs<T>::type>::type
-  operator|(const TVariant &variant, T defaultValue) {
+  friend
+      typename enable_if<!IsVariant<T>::value && !is_array<T>::value, T>::type
+      operator|(const TVariant &variant, const T &defaultValue) {
     if (variant.template is<T>())
       return variant.template as<T>();
     else
       return defaultValue;
   }
   //
+  // const char* operator|(JsonVariant, const char*)
+  friend const char *operator|(const TVariant &variant,
+                               const char *defaultValue) {
+    if (variant.template is<const char *>())
+      return variant.template as<const char *>();
+    else
+      return defaultValue;
+  }
+  //
   // JsonVariant operator|(JsonVariant, JsonVariant)
   template <typename T>
   friend typename enable_if<IsVariant<T>::value, typename T::variant_type>::type

+ 6 - 4
src/ArduinoJson/Variant/VariantRef.hpp

@@ -176,7 +176,7 @@ class VariantRef : public VariantRefBase<VariantData>,
 #endif
 
   template <typename T>
-  FORCE_INLINE typename VariantAs<T>::type as() const {
+  FORCE_INLINE T as() const {
     /********************************************************************
      **                THIS IS NOT A BUG IN THE LIBRARY                **
      **                --------------------------------                **
@@ -187,11 +187,13 @@ class VariantRef : public VariantRefBase<VariantData>,
      **  For example:                                                  **
      **    char* name = doc["name"];                                   **
      **    char age = doc["age"];                                      **
+     **    auto city = doc["city"].as<char*>()                         **
      **  Instead, use:                                                 **
      **    const char* name = doc["name"];                             **
      **    int8_t age = doc["age"];                                    **
+     **    auto city = doc["city"].as<const char*>()                   **
      ********************************************************************/
-    return variantAs<typename VariantAs<T>::type>(_data, _pool);
+    return variantAs<T>(_data, _pool);
   }
 
   template <typename T>
@@ -291,8 +293,8 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
   }
 
   template <typename T>
-  FORCE_INLINE typename VariantConstAs<T>::type as() const {
-    return variantAs<typename VariantConstAs<T>::type>(_data);
+  FORCE_INLINE T as() const {
+    return variantAs<T>(_data);
   }
 
   template <typename T>