diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a3bc949..e0e223cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ HEAD * Fixed `serializeJson(doc, String)` when allocation fails (issue #1572) * Fixed clang-tidy warnings (issue #1574, PR #1577 by @armandas) * Added fake class `InvalidConversion` to easily identify invalid conversions (issue #1585) +* Added support for `std::string_view` (issue #1578, PR #1554 by @0xFEEDC0DE64) v6.18.0 (2021-05-05) ------- diff --git a/extras/tests/CMakeLists.txt b/extras/tests/CMakeLists.txt index 6bd2eaf4..c88d3000 100644 --- a/extras/tests/CMakeLists.txt +++ b/extras/tests/CMakeLists.txt @@ -11,6 +11,7 @@ link_libraries(ArduinoJson catch) include_directories(Helpers) add_subdirectory(Cpp11) +add_subdirectory(Cpp17) add_subdirectory(FailingBuilds) add_subdirectory(IntegrationTests) add_subdirectory(JsonArray) diff --git a/extras/tests/Cpp17/CMakeLists.txt b/extras/tests/Cpp17/CMakeLists.txt new file mode 100644 index 00000000..80bf97b6 --- /dev/null +++ b/extras/tests/Cpp17/CMakeLists.txt @@ -0,0 +1,29 @@ +# ArduinoJson - https://arduinojson.org +# Copyright Benoit Blanchon 2014-2021 +# MIT License + +if(MSVC_VERSION LESS 1910) + return() +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5) + return() +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7) + return() +endif() + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(Cpp17Tests + string_view.cpp +) + +add_test(Cpp17 Cpp17Tests) + +set_tests_properties(Cpp17 + PROPERTIES + LABELS "Catch" +) diff --git a/extras/tests/Cpp17/string_view.cpp b/extras/tests/Cpp17/string_view.cpp new file mode 100644 index 00000000..7c00a4fa --- /dev/null +++ b/extras/tests/Cpp17/string_view.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include + +#if !ARDUINOJSON_ENABLE_STRING_VIEW +# error ARDUINOJSON_ENABLE_STRING_VIEW must be set to 1 +#endif + +TEST_CASE("string_view") { + StaticJsonDocument<128> doc; + JsonVariant variant = doc.to(); + + SECTION("deserializeJson()") { + auto err = deserializeJson(doc, std::string_view("123", 2)); + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.as() == 12); + } + + SECTION("JsonDocument::set()") { + doc.set(std::string_view("123", 2)); + REQUIRE(doc.as() == "12"); + } + + SECTION("JsonDocument::operator[]() const") { + doc["ab"] = "Yes"; + doc["abc"] = "No"; + REQUIRE(doc[std::string_view("abc", 2)] == "Yes"); + } + + SECTION("JsonDocument::operator[]()") { + doc[std::string_view("abc", 2)] = "Yes"; + REQUIRE(doc["ab"] == "Yes"); + } + + SECTION("JsonVariant::operator==()") { + variant.set("A"); + REQUIRE(variant == std::string_view("AX", 1)); + REQUIRE_FALSE(variant == std::string_view("BX", 1)); + } + + SECTION("JsonVariant::operator>()") { + variant.set("B"); + REQUIRE(variant > std::string_view("AX", 1)); + REQUIRE_FALSE(variant > std::string_view("CX", 1)); + } + + SECTION("JsonVariant::operator<()") { + variant.set("B"); + REQUIRE(variant < std::string_view("CX", 1)); + REQUIRE_FALSE(variant < std::string_view("AX", 1)); + } + + SECTION("String deduplication") { + doc.add(std::string_view("example one", 7)); + REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + 8); + + doc.add(std::string_view("example two", 7)); + REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8); + } +} + +using ARDUINOJSON_NAMESPACE::adaptString; + +TEST_CASE("StringViewAdapter") { + std::string_view str("bravoXXX", 5); + auto adapter = adaptString(str); + + CHECK(adapter.compare(NULL) > 0); + CHECK(adapter.compare("alpha") > 0); + CHECK(adapter.compare("bravo") == 0); + CHECK(adapter.compare("charlie") < 0); + + CHECK(adapter.equals("bravo")); + CHECK_FALSE(adapter.equals("charlie")); + CHECK_FALSE(adapter.equals(NULL)); + + CHECK(adapter.size() == 5); +} diff --git a/src/ArduinoJson/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index fca32187..332abb6a 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/src/ArduinoJson/Configuration.hpp @@ -62,6 +62,17 @@ # endif #endif +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +# ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +# define ARDUINOJSON_ENABLE_STRING_VIEW 1 +# endif +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +# define ARDUINOJSON_ENABLE_STRING_VIEW 0 +#endif + #if ARDUINOJSON_EMBEDDED_MODE // Store floats by default to reduce the memory usage (issue #134) diff --git a/src/ArduinoJson/Strings/StringAdapters.hpp b/src/ArduinoJson/Strings/StringAdapters.hpp index b587a387..1de86bcd 100644 --- a/src/ArduinoJson/Strings/StringAdapters.hpp +++ b/src/ArduinoJson/Strings/StringAdapters.hpp @@ -12,6 +12,10 @@ # include #endif +#if ARDUINOJSON_ENABLE_STRING_VIEW +# include +#endif + #if ARDUINOJSON_ENABLE_ARDUINO_STRING # include #endif diff --git a/src/ArduinoJson/Strings/StringViewAdapter.hpp b/src/ArduinoJson/Strings/StringViewAdapter.hpp new file mode 100644 index 00000000..4720660b --- /dev/null +++ b/src/ArduinoJson/Strings/StringViewAdapter.hpp @@ -0,0 +1,56 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include +#include +#include + +#include + +namespace ARDUINOJSON_NAMESPACE { + +class StringViewAdapter { + public: + StringViewAdapter(std::string_view str) : _str(str) {} + + void copyTo(char* p, size_t n) const { + memcpy(p, _str.data(), n); + } + + bool isNull() const { + return false; + } + + int compare(const char* other) const { + if (!other) + return 1; + return _str.compare(other); + } + + bool equals(const char* expected) const { + if (!expected) + return false; + return _str == expected; + } + + size_t size() const { + return _str.size(); + } + + typedef storage_policies::store_by_copy storage_policy; + + private: + std::string_view _str; +}; + +template <> +struct IsString : true_type {}; + +inline StringViewAdapter adaptString(const std::string_view& str) { + return StringViewAdapter(str); +} + +} // namespace ARDUINOJSON_NAMESPACE