From 8499f0b960749de8a8b8ef214f36ffb28967589e Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sun, 15 Jan 2017 15:11:26 +0100 Subject: [PATCH] Fixed errors with Variable Length Arrays (issue #416) --- CHANGELOG.md | 1 + .../Deserialization/JsonParser.hpp | 4 - include/ArduinoJson/JsonArray.hpp | 91 +++-- include/ArduinoJson/JsonArraySubscript.hpp | 38 +- include/ArduinoJson/JsonBuffer.hpp | 18 +- include/ArduinoJson/JsonBufferBase.hpp | 60 ++- include/ArduinoJson/JsonObject.hpp | 260 ++++++++++--- include/ArduinoJson/JsonObjectImpl.hpp | 20 +- include/ArduinoJson/JsonObjectSubscript.hpp | 67 ++-- include/ArduinoJson/JsonVariantBase.hpp | 36 +- .../ArduinoJson/StringTraits/CharPointer.hpp | 6 - .../{ConstRefOrConstPtr.hpp => IsArray.hpp} | 16 +- test/JsonArray_Subscript_Tests.cpp | 7 + test/TypeTraits_Tests.cpp | 6 + test/VariableLengthArray_Tests.cpp | 357 ++++++++++++++++++ 15 files changed, 822 insertions(+), 165 deletions(-) rename include/ArduinoJson/TypeTraits/{ConstRefOrConstPtr.hpp => IsArray.hpp} (60%) create mode 100644 test/VariableLengthArray_Tests.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index c1980393..c572cda4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ HEAD ---- * Fixed error when assigning a `volatile int` to a `JsonVariant` (issue #415) +* Fixed errors with Variable Length Arrays (issue #416) v5.8.0 ------ diff --git a/include/ArduinoJson/Deserialization/JsonParser.hpp b/include/ArduinoJson/Deserialization/JsonParser.hpp index e38a4d19..65e064be 100644 --- a/include/ArduinoJson/Deserialization/JsonParser.hpp +++ b/include/ArduinoJson/Deserialization/JsonParser.hpp @@ -94,10 +94,6 @@ struct JsonParserBuilder { } }; -template -struct JsonParserBuilder - : JsonParserBuilder {}; - template inline typename JsonParserBuilder::TParser makeParser( TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) { diff --git a/include/ArduinoJson/JsonArray.hpp b/include/ArduinoJson/JsonArray.hpp index 0b3f0529..949e336e 100644 --- a/include/ArduinoJson/JsonArray.hpp +++ b/include/ArduinoJson/JsonArray.hpp @@ -14,8 +14,8 @@ #include "JsonVariant.hpp" #include "Serialization/JsonPrintable.hpp" #include "StringTraits/StringTraits.hpp" -#include "TypeTraits/ConstRefOrConstPtr.hpp" #include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsArray.hpp" #include "TypeTraits/IsFloatingPoint.hpp" #include "TypeTraits/IsSame.hpp" @@ -58,73 +58,70 @@ class JsonArray : public Internals::JsonPrintable, // Adds the specified value at the end of the array. // - // bool add(bool); - // bool add(char); - // bool add(long); - // bool add(int); - // bool add(short); - // bool add(float value); - // bool add(double value); - // bool add(const char*); - // bool add(const char[]); - // bool add(const char[N]); - // bool add(RawJson); - // bool add(const std::string&) - // bool add(const String&) - // bool add(const JsonVariant&); - // bool add(JsonArray&); - // bool add(JsonObject&); + // bool add(TValue); + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template - bool add(const T &value) { - // reduce the number of template function instanciation to reduce code size - return addNodeImpl::type>(value); + typename TypeTraits::EnableIf::value, bool>::type add( + const T &value) { + return add_impl(value); } - // bool add(float value, uint8_t decimals); - // bool add(double value, uint8_t decimals); + // + // bool add(TValue); + // TValue = const char*, const char[N], const FlashStringHelper* + template + bool add(const T *value) { + return add_impl(value); + } + // + // bool add(TValue value, uint8_t decimals); + // TValue = float, double template bool add(T value, uint8_t decimals) { - return add(JsonVariant(value, decimals)); + return add_impl(JsonVariant(value, decimals)); } // Sets the value at specified index. // - // bool set(size_t index, bool value); - // bool set(size_t index, long value); - // bool set(size_t index, int value); - // bool set(size_t index, short value); - // bool set(size_t index, const std::string&) - // bool set(size_t index, const String&) - // bool set(size_t index, const JsonVariant&); - // bool set(size_t index, JsonArray&); - // bool set(size_t index, JsonObject&); + // bool add(size_t index, TValue); + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template - bool set(size_t index, const T &value) { - // reduce the number of template function instanciation to reduce code size - return setNodeAt::type>(index, - value); + typename TypeTraits::EnableIf::value, bool>::type set( + size_t index, const T &value) { + return set_impl(index, value); } - // bool set(size_t index, float value, uint8_t decimals = 2); - // bool set(size_t index, double value, uint8_t decimals = 2); + // + // bool add(size_t index, TValue); + // TValue = const char*, const char[N], const FlashStringHelper* + template + bool set(size_t index, const T *value) { + return set_impl(index, value); + } + // + // bool set(size_t index, TValue value, uint8_t decimals); + // TValue = float, double template typename TypeTraits::EnableIf::value, bool>::type set(size_t index, T value, uint8_t decimals) { - return set(index, JsonVariant(value, decimals)); + return set_impl(index, JsonVariant(value, decimals)); } // Gets the value at the specified index. template typename Internals::JsonVariantAs::type get(size_t index) const { - node_type *node = getNodeAt(index); + node_type *node = findNode(index); return node ? node->content.as() : Internals::JsonVariantDefault::get(); - ; } // Check the type of the value at specified index. template bool is(size_t index) const { - node_type *node = getNodeAt(index); + node_type *node = findNode(index); return node ? node->content.is() : false; } @@ -138,7 +135,7 @@ class JsonArray : public Internals::JsonPrintable, // Removes element at specified index. void removeAt(size_t index) { - removeNode(getNodeAt(index)); + removeNode(findNode(index)); } // Returns a reference an invalid JsonArray. @@ -203,15 +200,15 @@ class JsonArray : public Internals::JsonPrintable, } private: - node_type *getNodeAt(size_t index) const { + node_type *findNode(size_t index) const { node_type *node = _firstNode; while (node && index--) node = node->next; return node; } template - bool setNodeAt(size_t index, TValueRef value) { - node_type *node = getNodeAt(index); + bool set_impl(size_t index, TValueRef value) { + node_type *node = findNode(index); if (!node) return false; return Internals::ValueSetter::set(_buffer, node->content, @@ -219,7 +216,7 @@ class JsonArray : public Internals::JsonPrintable, } template - bool addNodeImpl(TValueRef value) { + bool add_impl(TValueRef value) { node_type *node = addNewNode(); if (!node) return false; diff --git a/include/ArduinoJson/JsonArraySubscript.hpp b/include/ArduinoJson/JsonArraySubscript.hpp index 111f193d..4bb38f15 100644 --- a/include/ArduinoJson/JsonArraySubscript.hpp +++ b/include/ArduinoJson/JsonArraySubscript.hpp @@ -26,11 +26,25 @@ class JsonArraySubscript : public JsonVariantBase { return *this; } + // Replaces the value + // + // operator=(TValue) + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template FORCE_INLINE JsonArraySubscript& operator=(const T& src) { _array.set(_index, src); return *this; } + // + // operator=(TValue) + // TValue = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE JsonArraySubscript& operator=(const T* src) { + _array.set(_index, src); + return *this; + } FORCE_INLINE bool success() const { return _index < _array.size(); @@ -46,9 +60,29 @@ class JsonArraySubscript : public JsonVariantBase { return _array.is(_index); } + // Replaces the value + // + // bool set(TValue) + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template - FORCE_INLINE void set(const TValue& value) { - _array.set(_index, value); + FORCE_INLINE bool set(const TValue& value) { + return _array.set(_index, value); + } + // + // bool set(TValue) + // TValue = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE bool set(const TValue* value) { + return _array.set(_index, value); + } + // + // bool set(TValue, uint8_t decimals); + // TValue = float, double + template + FORCE_INLINE bool set(const TValue& value, uint8_t decimals) { + return _array.set(_index, value, decimals); } private: diff --git a/include/ArduinoJson/JsonBuffer.hpp b/include/ArduinoJson/JsonBuffer.hpp index 0068b05b..e1c78774 100644 --- a/include/ArduinoJson/JsonBuffer.hpp +++ b/include/ArduinoJson/JsonBuffer.hpp @@ -12,6 +12,8 @@ #include #include "JsonVariant.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsArray.hpp" #if defined(__clang__) #pragma clang diagnostic push @@ -51,11 +53,23 @@ class JsonBuffer { // allocation fails. JsonObject &createObject(); - // Duplicate a string + // Duplicates a string + // + // char* strdup(TValue); + // TValue = const std::string&, const String&, template - char *strdup(const TString &src) { + typename TypeTraits::EnableIf::value, + char *>::type + strdup(const TString &src) { return Internals::StringFuncs::duplicate(src, this); } + // + // char* strdup(TValue); + // TValue = const char*, const char[N], const FlashStringHelper* + template + char *strdup(const TString *src) { + return Internals::StringFuncs::duplicate(src, this); + } // Allocates n bytes in the JsonBuffer. // Return a pointer to the allocated memory or NULL if allocation fails. diff --git a/include/ArduinoJson/JsonBufferBase.hpp b/include/ArduinoJson/JsonBufferBase.hpp index ba4cb2a9..7c393719 100644 --- a/include/ArduinoJson/JsonBufferBase.hpp +++ b/include/ArduinoJson/JsonBufferBase.hpp @@ -34,12 +34,27 @@ class JsonBufferBase : public JsonBuffer { // Returns a reference to the new JsonObject or JsonObject::invalid() if the // allocation fails. // With this overload, the JsonBuffer will make a copy of the string + // + // JsonArray& parseArray(TString); + // TString = const std::string&, const String& template - JsonArray &parseArray( - const TString &json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + typename TypeTraits::EnableIf::value, + JsonArray &>::type + parseArray(const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { return Internals::makeParser(that(), json, nestingLimit).parseArray(); } + // + // JsonArray& parseArray(TString); + // TString = const char*, const char[N], const FlashStringHelper* + template + JsonArray &parseArray( + TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseArray(); + } + // + // JsonArray& parseArray(TString); + // TString = std::istream&, Stream& template JsonArray &parseArray( TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { @@ -56,12 +71,27 @@ class JsonBufferBase : public JsonBuffer { // // Returns a reference to the new JsonObject or JsonObject::invalid() if the // allocation fails. + // + // JsonObject& parseObject(TString); + // TString = const std::string&, const String& template - JsonObject &parseObject( - const TString &json, - uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + typename TypeTraits::EnableIf::value, + JsonObject &>::type + parseObject(const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { return Internals::makeParser(that(), json, nestingLimit).parseObject(); } + // + // JsonObject& parseObject(TString); + // TString = const char*, const char[N], const FlashStringHelper* + template + JsonObject &parseObject( + TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseObject(); + } + // + // JsonObject& parseObject(TString); + // TString = std::istream&, Stream& template JsonObject &parseObject( TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { @@ -70,11 +100,27 @@ class JsonBufferBase : public JsonBuffer { // Generalized version of parseArray() and parseObject(), also works for // integral types. + // + // JsonVariant parse(TString); + // TString = const std::string&, const String& template - JsonVariant parse(const TString &json, + typename TypeTraits::EnableIf::value, + JsonVariant>::type + parse(const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseVariant(); + } + // + // JsonVariant parse(TString); + // TString = const char*, const char[N], const FlashStringHelper* + template + JsonVariant parse(TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { return Internals::makeParser(that(), json, nestingLimit).parseVariant(); } + // + // JsonVariant parse(TString); + // TString = std::istream&, Stream& template JsonVariant parse(TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { diff --git a/include/ArduinoJson/JsonObject.hpp b/include/ArduinoJson/JsonObject.hpp index 24e74e15..873289fe 100644 --- a/include/ArduinoJson/JsonObject.hpp +++ b/include/ArduinoJson/JsonObject.hpp @@ -14,8 +14,8 @@ #include "JsonPair.hpp" #include "Serialization/JsonPrintable.hpp" #include "StringTraits/StringTraits.hpp" -#include "TypeTraits/ConstRefOrConstPtr.hpp" #include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsArray.hpp" #include "TypeTraits/IsFloatingPoint.hpp" #include "TypeTraits/IsSame.hpp" @@ -47,81 +47,226 @@ class JsonObject : public Internals::JsonPrintable, explicit JsonObject(JsonBuffer* buffer) : Internals::List(buffer) {} // Gets or sets the value associated with the specified key. + // + // JsonObjectSubscript operator[](TKey) + // TKey = const std::string&, const String& template - JsonObjectSubscript operator[](const TString& key); + typename TypeTraits::EnableIf::value, + JsonObjectSubscript >::type + operator[](const TString& key) { + return JsonObjectSubscript(*this, key); + } + // + // JsonObjectSubscript operator[](TKey) + // TKey = const char*, const char[N], const FlashStringHelper* + template + JsonObjectSubscript operator[](const TString* key) { + return JsonObjectSubscript(*this, key); + } // Gets the value associated with the specified key. + // + // JsonVariant operator[](TKey) const; + // TKey = const std::string&, const String& template - JsonVariant operator[](const TString& key) const { - return get(key); + const JsonVariant operator[](const TString& key) const { + return get_impl(key); + } + // + // JsonVariant operator[](TKey) const; + // TKey = const char*, const char[N], const FlashStringHelper* + template + const JsonVariant operator[](const TString* key) const { + return get_impl(key); } // Sets the specified key with the specified value. - // bool set(TKey key, bool value); - // bool set(TKey key, char value); - // bool set(TKey key, long value); - // bool set(TKey key, int value); - // bool set(TKey key, short value); - // bool set(TKey key, float value); - // bool set(TKey key, double value); - // bool set(TKey key, const char* value); - // bool set(TKey key, RawJson value); - // bool set(Key, String&); - // bool set(Key, JsonArray&); - // bool set(Key, JsonObject&); - // bool set(Key, JsonVariant&); + // + // bool set(TKey, TValue); + // TKey = const std::string&, const String& + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template - bool set(const TString& key, const TValue& value) { - // reduce the number of template function instanciation to reduce code size - return setNodeAt::type, - typename TypeTraits::ConstRefOrConstPtr::type>( - key, value); + typename TypeTraits::EnableIf::value && + !TypeTraits::IsArray::value, + bool>::type + set(const TString& key, const TValue& value) { + return set_impl(key, value); } - // bool set(Key, float value, uint8_t decimals); - // bool set(Key, double value, uint8_t decimals); + // + // bool set(TKey, TValue); + // TKey = const std::string&, const String& + // TValue = const char*, const char[N], const FlashStringHelper* + template + typename TypeTraits::EnableIf::value, + bool>::type + set(const TString& key, const TValue* value) { + return set_impl(key, value); + } + // + // bool set(TKey, TValue); + // TKey = const char*, const char[N], const FlashStringHelper* + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& + template + typename TypeTraits::EnableIf::value, bool>::type + set(const TString* key, const TValue& value) { + return set_impl(key, value); + } + // + // bool set(TKey, TValue); + // TKey = const char*, const char[N], const FlashStringHelper* + // TValue = const char*, const char[N], const FlashStringHelper* + template + bool set(const TString* key, const TValue* value) { + return set_impl(key, value); + } + // + // bool set(TKey, TValue, uint8_t decimals); + // TKey = const std::string&, const String& + // TValue = float, double + template + typename TypeTraits::EnableIf::value && + !TypeTraits::IsArray::value, + bool>::type + set(const TString& key, TValue value, uint8_t decimals) { + return set_impl( + key, JsonVariant(value, decimals)); + } + // + // bool set(TKey, TValue, uint8_t decimals); + // TKey = const char*, const char[N], const FlashStringHelper* + // TValue = float, double template typename TypeTraits::EnableIf::value, bool>::type - set(const TString& key, TValue value, uint8_t decimals) { - return set(key, JsonVariant(value, decimals)); + set(const TString* key, TValue value, uint8_t decimals) { + return set_impl( + key, JsonVariant(value, decimals)); } // Gets the value associated with the specified key. + // + // TValue get(TKey); + // TKey = const std::string&, const String& + // TValue = bool, char, long, int, short, float, double, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& + template + typename TypeTraits::EnableIf< + !TypeTraits::IsArray::value, + typename Internals::JsonVariantAs::type>::type + get(const TString& key) const { + return get_impl(key); + } + // + // TValue get(TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + // TValue = bool, char, long, int, short, float, double, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template typename Internals::JsonVariantAs::type get( - const TString& key) const { - node_type* node = getNodeAt(key); - return node ? node->content.value.as() - : Internals::JsonVariantDefault::get(); + const TString* key) const { + return get_impl(key); } // Checks the type of the value associated with the specified key. + // + // + // bool is(TKey) const; + // TKey = const std::string&, const String& + // TValue = bool, char, long, int, short, float, double, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template - bool is(const TString& key) const { - node_type* node = getNodeAt(key); - return node ? node->content.value.is() : false; + typename TypeTraits::EnableIf::value, + bool>::type + is(const TString& key) const { + return is_impl(key); + } + // + // bool is(TKey) const; + // TKey = const char*, const char[N], const FlashStringHelper* + // TValue = bool, char, long, int, short, float, double, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& + template + bool is(const TString* key) const { + return is_impl(key); } // Creates and adds a JsonArray. - // This is a shortcut for JsonBuffer::createArray() and JsonObject::add(). + // + // JsonArray& createNestedArray(TKey); + // TKey = const std::string&, const String& template - JsonArray& createNestedArray(const TString& key); + typename TypeTraits::EnableIf::value, + JsonArray&>::type + createNestedArray(const TString& key) { + return createNestedArray_impl(key); + } + // JsonArray& createNestedArray(TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + JsonArray& createNestedArray(const TString* key) { + return createNestedArray_impl(key); + } // Creates and adds a JsonObject. - // This is a shortcut for JsonBuffer::createObject() and JsonObject::add(). + // + // JsonObject& createNestedObject(TKey); + // TKey = const std::string&, const String& template - JsonObject& createNestedObject(const TString& key); + typename TypeTraits::EnableIf::value, + JsonObject&>::type + createNestedObject(const TString& key) { + return createNestedObject_impl(key); + } + // + // JsonObject& createNestedObject(TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + JsonObject& createNestedObject(const TString* key) { + return createNestedObject_impl(key); + } // Tells weither the specified key is present and associated with a value. + // + // bool containsKey(TKey); + // TKey = const std::string&, const String& template - bool containsKey(const TString& key) const { - return getNodeAt(key) != NULL; + typename TypeTraits::EnableIf::value, + bool>::type + containsKey(const TString& key) const { + return findNode(key) != NULL; + } + // + // bool containsKey(TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + bool containsKey(const TString* key) const { + return findNode(key) != NULL; } // Removes the specified key and the associated value. + // + // void remove(TKey); + // TKey = const std::string&, const String& template - void remove(const TString& key) { - removeNode(getNodeAt(key)); + typename TypeTraits::EnableIf::value, + void>::type + remove(const TString& key) { + removeNode(findNode(key)); + } + // + // void remove(TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + void remove(const TString* key) { + removeNode(findNode(key)); } // Returns a reference an invalid JsonObject. @@ -134,15 +279,8 @@ class JsonObject : public Internals::JsonPrintable, private: // Returns the list node that matches the specified key. - template - node_type* getNodeAt(const TString& key) const { - // reduce the number of template function instanciation to reduce code size - return getNodeAtImpl< - typename TypeTraits::ConstRefOrConstPtr::type>(key); - } - template - node_type* getNodeAtImpl(TStringRef key) const { + node_type* findNode(TStringRef key) const { for (node_type* node = _firstNode; node; node = node->next) { if (Internals::StringFuncs::equals(key, node->content.key)) return node; @@ -150,9 +288,17 @@ class JsonObject : public Internals::JsonPrintable, return NULL; } + template + typename Internals::JsonVariantAs::type get_impl( + TStringRef key) const { + node_type* node = findNode(key); + return node ? node->content.value.as() + : Internals::JsonVariantDefault::get(); + } + template - bool setNodeAt(TStringRef key, TValueRef value) { - node_type* node = getNodeAtImpl(key); + bool set_impl(TStringRef key, TValueRef value) { + node_type* node = findNode(key); if (!node) { node = addNewNode(); if (!node) return false; @@ -164,6 +310,18 @@ class JsonObject : public Internals::JsonPrintable, return Internals::ValueSetter::set(_buffer, node->content.value, value); } + + template + bool is_impl(TStringRef key) const { + node_type* node = findNode(key); + return node ? node->content.value.is() : false; + } + + template + JsonArray& createNestedArray_impl(TStringRef key); + + template + JsonObject& createNestedObject_impl(TStringRef key); }; namespace Internals { diff --git a/include/ArduinoJson/JsonObjectImpl.hpp b/include/ArduinoJson/JsonObjectImpl.hpp index 47dcce58..5f336dd7 100644 --- a/include/ArduinoJson/JsonObjectImpl.hpp +++ b/include/ArduinoJson/JsonObjectImpl.hpp @@ -13,19 +13,19 @@ namespace ArduinoJson { -template -inline JsonObject &JsonObject::createNestedObject(const TString &key) { - if (!_buffer) return JsonObject::invalid(); - JsonObject &object = _buffer->createObject(); - set(key, object); - return object; -} - -template -inline JsonArray &JsonObject::createNestedArray(const TString &key) { +template +inline JsonArray &JsonObject::createNestedArray_impl(TStringRef key) { if (!_buffer) return JsonArray::invalid(); JsonArray &array = _buffer->createArray(); set(key, array); return array; } + +template +inline JsonObject &JsonObject::createNestedObject_impl(TStringRef key) { + if (!_buffer) return JsonObject::invalid(); + JsonObject &object = _buffer->createObject(); + set(key, object); + return object; +} } diff --git a/include/ArduinoJson/JsonObjectSubscript.hpp b/include/ArduinoJson/JsonObjectSubscript.hpp index 2dcecb9f..6c62cbac 100644 --- a/include/ArduinoJson/JsonObjectSubscript.hpp +++ b/include/ArduinoJson/JsonObjectSubscript.hpp @@ -9,7 +9,6 @@ #include "Configuration.hpp" #include "JsonVariantBase.hpp" -#include "TypeTraits/ConstRefOrConstPtr.hpp" #include "TypeTraits/EnableIf.hpp" #ifdef _MSC_VER @@ -19,26 +18,39 @@ namespace ArduinoJson { -template +template class JsonObjectSubscript - : public JsonVariantBase > { - // const String& - // const std::string& - // const char* - typedef typename TypeTraits::ConstRefOrConstPtr::type TStringRef; + : public JsonVariantBase > { + typedef JsonObjectSubscript this_type; public: FORCE_INLINE JsonObjectSubscript(JsonObject& object, TStringRef key) : _object(object), _key(key) {} - FORCE_INLINE JsonObjectSubscript& operator=( - const JsonObjectSubscript& src) { + FORCE_INLINE this_type& operator=(const this_type& src) { _object.set(_key, src); return *this; } - template - FORCE_INLINE JsonObjectSubscript& operator=(const T& src) { + // Set the specified value + // + // operator=(TValue); + // TValue = bool, char, long, int, short, float, double, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& + template + FORCE_INLINE + typename TypeTraits::EnableIf::value, + this_type&>::type + operator=(const TValue& src) { + _object.set(_key, src); + return *this; + } + // + // operator=(TValue); + // TValue = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE this_type& operator=(const TValue* src) { _object.set(_key, src); return *this; } @@ -49,7 +61,7 @@ class JsonObjectSubscript template FORCE_INLINE typename Internals::JsonVariantAs::type as() const { - return _object.get(_key); + return _object.get(_key); } template @@ -57,11 +69,29 @@ class JsonObjectSubscript return _object.is(_key); } + // Sets the specified value. + // + // bool set(TValue); + // TValue = bool, char, long, int, short, float, double, RawJson, JsonVariant, + // const std::string&, const String&, + // const JsonArray&, const JsonObject& template - FORCE_INLINE bool set(const TValue& value) { + FORCE_INLINE + typename TypeTraits::EnableIf::value, + bool>::type + set(const TValue& value) { return _object.set(_key, value); } - + // + // bool set(TValue); + // TValue = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE bool set(const TValue* value) { + return _object.set(_key, value); + } + // + // bool set(TValue, uint8_t decimals); + // TValue = float, double template FORCE_INLINE bool set(const TValue& value, uint8_t decimals) { return _object.set(_key, value, decimals); @@ -73,17 +103,12 @@ class JsonObjectSubscript }; #if ARDUINOJSON_ENABLE_STD_STREAM -template +template inline std::ostream& operator<<(std::ostream& os, - const JsonObjectSubscript& source) { + const JsonObjectSubscript& source) { return source.printTo(os); } #endif - -template -inline JsonObjectSubscript JsonObject::operator[](const TString& key) { - return JsonObjectSubscript(*this, key); -} } // namespace ArduinoJson #ifdef _MSC_VER diff --git a/include/ArduinoJson/JsonVariantBase.hpp b/include/ArduinoJson/JsonVariantBase.hpp index 503da2e9..7c9a9e5e 100644 --- a/include/ArduinoJson/JsonVariantBase.hpp +++ b/include/ArduinoJson/JsonVariantBase.hpp @@ -78,20 +78,46 @@ class JsonVariantBase : public Internals::JsonPrintable { // Returns the value associated with the specified key if the variant is // an object. // Return JsonVariant::invalid() if the variant is not an object. + // + // const JsonObjectSubscript operator[](TKey) const; + // TKey = const std::string&, const String& template - FORCE_INLINE - typename TypeTraits::EnableIf::has_equals, - const JsonObjectSubscript >::type - operator[](const TString &key) const { + FORCE_INLINE typename TypeTraits::EnableIf< + Internals::StringFuncs::has_equals, + const JsonObjectSubscript >::type + operator[](const TString &key) const { return asObject()[key]; } + // + // const JsonObjectSubscript operator[](TKey) const; + // TKey = const char*, const char[N], const FlashStringHelper* template FORCE_INLINE typename TypeTraits::EnableIf::has_equals, - JsonObjectSubscript >::type + JsonObjectSubscript >::type operator[](const TString &key) { return asObject()[key]; } + // + // JsonObjectSubscript operator[](TKey); + // TKey = const std::string&, const String& + template + FORCE_INLINE typename TypeTraits::EnableIf< + Internals::StringFuncs::has_equals, + JsonObjectSubscript >::type + operator[](const TString *key) { + return asObject()[key]; + } + // + // JsonObjectSubscript operator[](TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE typename TypeTraits::EnableIf< + Internals::StringFuncs::has_equals, + const JsonObjectSubscript >::type + operator[](const TString *key) const { + return asObject()[key]; + } private: const TImpl *impl() const { diff --git a/include/ArduinoJson/StringTraits/CharPointer.hpp b/include/ArduinoJson/StringTraits/CharPointer.hpp index 16e8f4c2..d04c5f71 100644 --- a/include/ArduinoJson/StringTraits/CharPointer.hpp +++ b/include/ArduinoJson/StringTraits/CharPointer.hpp @@ -47,11 +47,5 @@ struct StringFuncs : CharPtrFuncs {}; template <> struct StringFuncs : CharPtrFuncs {}; - -template -struct StringFuncs : CharPtrFuncs {}; - -template -struct StringFuncs : CharPtrFuncs {}; } } diff --git a/include/ArduinoJson/TypeTraits/ConstRefOrConstPtr.hpp b/include/ArduinoJson/TypeTraits/IsArray.hpp similarity index 60% rename from include/ArduinoJson/TypeTraits/ConstRefOrConstPtr.hpp rename to include/ArduinoJson/TypeTraits/IsArray.hpp index 19d83708..e8627088 100644 --- a/include/ArduinoJson/TypeTraits/ConstRefOrConstPtr.hpp +++ b/include/ArduinoJson/TypeTraits/IsArray.hpp @@ -12,20 +12,16 @@ namespace TypeTraits { // A meta-function that return the type T without the const modifier template -struct ConstRefOrConstPtr { - typedef const T& type; +struct IsArray { + static const bool value = false; }; template -struct ConstRefOrConstPtr { - typedef const T* type; -}; -template -struct ConstRefOrConstPtr { - typedef const T* type; +struct IsArray { + static const bool value = true; }; template -struct ConstRefOrConstPtr { - typedef const T* type; +struct IsArray { + static const bool value = true; }; } } diff --git a/test/JsonArray_Subscript_Tests.cpp b/test/JsonArray_Subscript_Tests.cpp index eead7117..d123915a 100644 --- a/test/JsonArray_Subscript_Tests.cpp +++ b/test/JsonArray_Subscript_Tests.cpp @@ -49,6 +49,13 @@ TEST_(StoreDouble) { EXPECT_FALSE(_array[0].is()); } +TEST_(StoreDoubleWithDecimals) { + _array[0].set(123.45, 2); + EXPECT_EQ(123.45, _array[0].as()); + EXPECT_TRUE(_array[0].is()); + EXPECT_FALSE(_array[0].is()); +} + TEST_(StoreBoolean) { _array[0] = true; EXPECT_EQ(true, _array[0].as()); diff --git a/test/TypeTraits_Tests.cpp b/test/TypeTraits_Tests.cpp index 653df8d8..12a320da 100644 --- a/test/TypeTraits_Tests.cpp +++ b/test/TypeTraits_Tests.cpp @@ -15,3 +15,9 @@ TEST(StdStream, IsBaseOf) { ASSERT_FALSE((IsBaseOf::value)); ASSERT_TRUE((IsBaseOf::value)); } + +TEST(StdStream, IsArray) { + ASSERT_FALSE((IsArray::value)); + ASSERT_TRUE((IsArray::value)); + ASSERT_TRUE((IsArray::value)); +} diff --git a/test/VariableLengthArray_Tests.cpp b/test/VariableLengthArray_Tests.cpp new file mode 100644 index 00000000..68acd209 --- /dev/null +++ b/test/VariableLengthArray_Tests.cpp @@ -0,0 +1,357 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wvla-extension" +#define CONFLICTS_WITH_BUILTIN_OPERATOR +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wvla" +#else +#define VLA_NOT_SUPPORTED +#endif + +#ifndef VLA_NOT_SUPPORTED + +TEST(VariableLengthArray, ParseArray) { + int i = 8; + char vla[i]; + strcpy(vla, "[42]"); + + StaticJsonBuffer jsonBuffer; + JsonArray& arr = jsonBuffer.parseArray(vla); + + EXPECT_TRUE(arr.success()); +} + +TEST(VariableLengthArray, ParseObject) { + int i = 16; + char vla[i]; + strcpy(vla, "{\"a\":42}"); + + StaticJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.parseObject(vla); + + EXPECT_TRUE(obj.success()); +} + +TEST(VariableLengthArray, Parse) { + int i = 16; + char vla[i]; + strcpy(vla, "42"); + + StaticJsonBuffer<1> jsonBuffer; + JsonVariant variant = jsonBuffer.parse(vla); + + EXPECT_EQ(42, variant.as()); +} + +TEST(VariableLengthArray, JsonVariant_Constructor) { + int i = 16; + char vla[i]; + strcpy(vla, "42"); + + JsonVariant variant(vla); + + EXPECT_EQ(42, variant.as()); +} + +TEST(VariableLengthArray, JsonVariant_Assign) { + int i = 16; + char vla[i]; + strcpy(vla, "42"); + + JsonVariant variant(666); + variant = vla; + + EXPECT_EQ(42, variant.as()); +} + +#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR +TEST(VariableLengthArray, JsonVariant_Subscript) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonVariant variant = jsonBuffer.parseObject("{\"hello\":\"world\"}"); + + EXPECT_STREQ("world", variant[vla]); +} +#endif + +#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR +TEST(VariableLengthArray, JsonVariant_Subscript_Const) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + const JsonVariant variant = jsonBuffer.parseObject("{\"hello\":\"world\"}"); + + EXPECT_STREQ("world", variant[vla]); +} +#endif + +TEST(VariableLengthArray, JsonVariant_Equals) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + const JsonVariant variant = "hello"; + + EXPECT_TRUE(vla == variant); + EXPECT_TRUE(variant == vla); + EXPECT_FALSE(vla != variant); + EXPECT_FALSE(variant != vla); +} + +TEST(VariableLengthArray, JsonVariant_Differs) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + const JsonVariant variant = "world"; + + EXPECT_TRUE(vla != variant); + EXPECT_TRUE(variant != vla); + EXPECT_FALSE(vla == variant); + EXPECT_FALSE(variant == vla); +} + +#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR +TEST(VariableLengthArray, JsonObject_Subscript) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj[vla] = "world"; + + EXPECT_STREQ("world", obj["hello"]); +} +#endif + +TEST(VariableLengthArray, JsonObject_Subscript_Assign) { // issue #416 + int i = 32; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj["hello"] = vla; + + EXPECT_STREQ("world", obj["hello"].as()); +} + +TEST(VariableLengthArray, JsonObject_Subscript_Set) { + int i = 32; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj["hello"].set(vla); + + EXPECT_STREQ("world", obj["hello"].as()); +} + +#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR +TEST(VariableLengthArray, JsonObject_Subscript_Const) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + const JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}"); + + EXPECT_STREQ("world", obj[vla]); +} +#endif + +TEST(VariableLengthArray, JsonObject_Get) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}"); + + EXPECT_STREQ("world", obj.get(vla)); +} + +TEST(VariableLengthArray, JsonObject_Set_Key) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj.set(vla, "world"); + + EXPECT_STREQ("world", obj["hello"]); +} + +TEST(VariableLengthArray, JsonObject_Set_Value) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj.set("hello", vla); + + EXPECT_STREQ("world", obj["hello"]); +} + +TEST(VariableLengthArray, JsonObject_Set_Key_WithDecimals) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj.set(vla, 3.14, 2); + + EXPECT_EQ(3.14, obj["hello"]); +} + +TEST(VariableLengthArray, JsonObject_Set_KeyAndValue) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj.set(vla, vla); + + EXPECT_STREQ("world", obj["world"]); +} + +TEST(VariableLengthArray, JsonObject_ContainsKey) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + const JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}"); + + EXPECT_TRUE(obj.containsKey(vla)); +} + +TEST(VariableLengthArray, JsonObject_Remove) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}"); + obj.remove(vla); + + EXPECT_EQ(0, obj.size()); +} + +TEST(VariableLengthArray, JsonObject_Is) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.parseObject("{\"hello\":42}"); + + EXPECT_TRUE(obj.is(vla)); +} + +TEST(VariableLengthArray, JsonObject_CreateNestedArray) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj.createNestedArray(vla); +} + +TEST(VariableLengthArray, JsonObject_CreateNestedObject) { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonBuffer jsonBuffer; + JsonObject& obj = jsonBuffer.createObject(); + obj.createNestedObject(vla); +} + +TEST(VariableLengthArray, JsonArray_Add) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonArray& arr = jsonBuffer.createArray(); + arr.add(vla); + + EXPECT_STREQ("world", arr[0]); +} + +TEST(VariableLengthArray, JsonArray_Set) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonArray& arr = jsonBuffer.createArray(); + arr.add("hello"); + arr.set(0, vla); + + EXPECT_STREQ("world", arr[0]); +} + +TEST(VariableLengthArray, JsonArraySubscript_Set) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonArray& arr = jsonBuffer.createArray(); + arr.add("hello"); + arr[0].set(vla); + + EXPECT_STREQ("world", arr[0]); +} + +TEST(VariableLengthArray, JsonArraySubscript_Assign) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + JsonArray& arr = jsonBuffer.createArray(); + arr.add("hello"); + arr[0] = vla; + + EXPECT_STREQ("world", arr[0]); +} + +TEST(VariableLengthArray, JsonBuffer_strdup) { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonBuffer jsonBuffer; + const char* dup = jsonBuffer.strdup(vla); + + EXPECT_NE(static_cast(vla), static_cast(dup)); + EXPECT_STREQ("world", dup); +} + +#endif