From 225c8f150ad6b098de6a650fc0cce613d5d66802 Mon Sep 17 00:00:00 2001 From: Guillaume Racicot Date: Sat, 6 Jun 2020 11:36:39 -0400 Subject: [PATCH 01/49] Disable std::swap specialization in C++20 and added a friend swap function --- include/nlohmann/detail/macro_scope.hpp | 6 +++- include/nlohmann/json.hpp | 33 +++++++++++++++++++++ single_include/nlohmann/json.hpp | 39 ++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 25e6b76eb..a48d18e89 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -20,7 +20,11 @@ #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 592d19667..abc530566 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -5865,6 +5865,34 @@ class basic_json /*! @brief exchanges the values + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is @@ -8635,6 +8663,9 @@ struct less<::nlohmann::detail::value_t> } }; +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + /*! @brief exchanges the values of two JSON objects @@ -8649,6 +8680,8 @@ inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcep j1.swap(j2); } +#endif + } // namespace std /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a24a08b5a..53a9380cd 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2048,7 +2048,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) @@ -21628,6 +21632,34 @@ class basic_json /*! @brief exchanges the values + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is @@ -24398,6 +24430,9 @@ struct less<::nlohmann::detail::value_t> } }; +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + /*! @brief exchanges the values of two JSON objects @@ -24412,6 +24447,8 @@ inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcep j1.swap(j2); } +#endif + } // namespace std /*! From 82fbbeeac5c4a75fa4af95f10c97c56ef95ddbed Mon Sep 17 00:00:00 2001 From: Guillaume Racicot Date: Sat, 6 Jun 2020 12:28:52 -0400 Subject: [PATCH 02/49] Adapted unit tests to use ADL calls for swap like the new swappable concept --- test/src/unit-concepts.cpp | 4 ++-- test/src/unit-modifiers.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index 899d1eaf9..1f3afc0c9 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -154,7 +154,7 @@ TEST_CASE("concepts") json j {1, 2, 3}; json::iterator it1 = j.begin(); json::iterator it2 = j.end(); - std::swap(it1, it2); + swap(it1, it2); CHECK(it1 == j.end()); CHECK(it2 == j.begin()); } @@ -162,7 +162,7 @@ TEST_CASE("concepts") json j {1, 2, 3}; json::const_iterator it1 = j.cbegin(); json::const_iterator it2 = j.cend(); - std::swap(it1, it2); + swap(it1, it2); CHECK(it1 == j.end()); CHECK(it2 == j.begin()); } diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 9214c6082..4b8d07621 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -878,7 +878,8 @@ TEST_CASE("modifiers") json j("hello world"); json k(42.23); - std::swap(j, k); + using std::swap; + swap(j, k); CHECK(j == json(42.23)); CHECK(k == json("hello world")); From 71830be06d80b08a0a1eda8a34c063246c923792 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 10 Jun 2020 19:27:28 +0800 Subject: [PATCH 03/49] fix issue#1275 --- include/nlohmann/json.hpp | 25 +++++++++++++++++++++++++ single_include/nlohmann/json.hpp | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index d1dc41192..0deb05833 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3788,6 +3788,31 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } + /*! + @brief overload for a default value of type rvalue + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + template>::value + and not std::is_same::value, int>::type = 0> + detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && + { + // only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return std::move(it->template get_ref()); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index dd9b21c4f..9fd621c79 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19566,6 +19566,31 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } + /*! + @brief overload for a default value of type rvalue + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + template>::value + and not std::is_same::value, int>::type = 0> + detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && + { + // only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return std::move(it->template get_ref()); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const From a3df26b771cbe866e000615300302e2be22eb8a4 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 10 Jun 2020 19:27:57 +0800 Subject: [PATCH 04/49] add some test cases --- test/src/unit-element_access2.cpp | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index e881a9479..6df0ca59a 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -148,6 +148,45 @@ TEST_CASE("element access 2") SECTION("access specified element with default value") { + SECTION("move semantics") + { + SECTION("json is rvalue") + { + json j = {{"x", "123"}}; + std::string defval = "default"; + auto val = std::move(j).value("x", defval); + + CHECK(j["x"] == ""); + CHECK(defval == "default"); + CHECK(val == "123"); + } + + SECTION("default is rvalue") + { + json j = {{"x", "123"}}; + std::string defval = "default"; + auto val = std::move(j).value("y", std::move(defval)); + + CHECK(j["x"] == "123"); + CHECK(defval == ""); + CHECK(val == "default"); + } + + SECTION("access on non-object value") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + std::string defval = "default"; + + CHECK_THROWS_AS(std::move(j_nonobject).value("foo", defval), json::type_error&); + CHECK_THROWS_AS(std::move(j_nonobject_const).value("foo", defval), json::type_error&); + CHECK_THROWS_WITH(std::move(j_nonobject).value("foo", defval), + "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH(std::move(j_nonobject_const).value("foo", defval), + "[json.exception.type_error.306] cannot use value() with array"); + } + } + SECTION("given a key") { SECTION("access existing value") From 691fb0c57a142889f788c72e8b7d64548cb5b06c Mon Sep 17 00:00:00 2001 From: chenguoping Date: Tue, 16 Jun 2020 15:35:26 +0800 Subject: [PATCH 05/49] fix issue#2059 --- include/nlohmann/detail/iterators/iteration_proxy.hpp | 3 ++- single_include/nlohmann/json.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index c61d96296..48927b018 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -12,7 +12,8 @@ namespace nlohmann { namespace detail { -template +template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { target = std::to_string(value); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543..69a4ec092 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3650,7 +3650,8 @@ namespace nlohmann { namespace detail { -template +template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { target = std::to_string(value); From b53c6e2f8193cb6469ff4319f6c516ca94de71d3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 16 Jun 2020 12:28:59 +0200 Subject: [PATCH 06/49] :sparkles: ignore comments --- include/nlohmann/detail/input/lexer.hpp | 88 ++++++++++++++++++++++++- single_include/nlohmann/json.hpp | 88 ++++++++++++++++++++++++- test/src/unit-class_lexer.cpp | 20 ++++++ 3 files changed, 190 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 0ff0c7362..00af0ce21 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -112,8 +112,11 @@ class lexer : public lexer_base public: using token_type = typename lexer_base::token_type; - explicit lexer(InputAdapterType&& adapter) - : ia(std::move(adapter)), decimal_point_char(static_cast(get_decimal_point())) {} + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} // delete because of pointer members lexer(const lexer&) = delete; @@ -131,7 +134,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -826,6 +829,71 @@ class lexer : public lexer_base } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + // remember character after '/' to distinguish comment types + const auto comment_char = get(); + + // expect // or /* to start a comment + if (comment_char != '/' and comment_char != '*') + { + return false; + } + + while (true) + { + switch (get()) + { + // EOF inside a /* comment is an error, in // it is OK + case std::char_traits::eof(): + case '\0': + { + return comment_char == '/'; + } + + // a newline ends the // comment + case '\n': + case '\r': + { + if (comment_char == '/') + { + return true; + } + break; + } + + // */ ends the /* comment + case '*': + { + if (comment_char == '*') + { + switch (get()) + { + case '/': + { + return true; + } + + default: + { + unget(); + break; + } + } + } + break; + } + + default: + break; + } + } + } + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { @@ -1431,6 +1499,17 @@ scan_number_done: } while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + // ignore comments + if (ignore_comments and current == '/') + { + if (not scan_comment()) + { + error_message = "invalid comment"; + return token_type::parse_error; + } + get(); + } + switch (current) { // structural characters @@ -1499,6 +1578,9 @@ scan_number_done: /// input adapter InputAdapterType ia; + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + /// the current character char_int_type current = std::char_traits::eof(); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543..82435fa7a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8179,8 +8179,11 @@ class lexer : public lexer_base public: using token_type = typename lexer_base::token_type; - explicit lexer(InputAdapterType&& adapter) - : ia(std::move(adapter)), decimal_point_char(static_cast(get_decimal_point())) {} + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} // delete because of pointer members lexer(const lexer&) = delete; @@ -8198,7 +8201,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -8893,6 +8896,71 @@ class lexer : public lexer_base } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + // remember character after '/' to distinguish comment types + const auto comment_char = get(); + + // expect // or /* to start a comment + if (comment_char != '/' and comment_char != '*') + { + return false; + } + + while (true) + { + switch (get()) + { + // EOF inside a /* comment is an error, in // it is OK + case std::char_traits::eof(): + case '\0': + { + return comment_char == '/'; + } + + // a newline ends the // comment + case '\n': + case '\r': + { + if (comment_char == '/') + { + return true; + } + break; + } + + // */ ends the /* comment + case '*': + { + if (comment_char == '*') + { + switch (get()) + { + case '/': + { + return true; + } + + default: + { + unget(); + break; + } + } + } + break; + } + + default: + break; + } + } + } + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { @@ -9498,6 +9566,17 @@ scan_number_done: } while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + // ignore comments + if (ignore_comments and current == '/') + { + if (not scan_comment()) + { + error_message = "invalid comment"; + return token_type::parse_error; + } + get(); + } + switch (current) { // structural characters @@ -9566,6 +9645,9 @@ scan_number_done: /// input adapter InputAdapterType ia; + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + /// the current character char_int_type current = std::char_traits::eof(); diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 0e1b53785..c4423e602 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -127,6 +127,8 @@ TEST_CASE("lexer class") // store scan() result const auto res = scan_string(s.c_str()); + CAPTURE(s); + switch (c) { // single characters that are valid tokens @@ -161,6 +163,9 @@ TEST_CASE("lexer class") break; } + // case ('/'): + // break; + // anything else is not expected default: { @@ -179,4 +184,19 @@ TEST_CASE("lexer class") s += "\""; CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string)); } + + // SECTION("ignore comments") + // { + // CHECK((scan_string("/") == json::lexer::token_type::parse_error)); + // + // CHECK((scan_string("/!") == json::lexer::token_type::parse_error)); + // CHECK((scan_string("/*") == json::lexer::token_type::parse_error)); + // CHECK((scan_string("/**") == json::lexer::token_type::parse_error)); + // + // CHECK((scan_string("//") == json::lexer::token_type::end_of_input)); + // CHECK((scan_string("/**/") == json::lexer::token_type::end_of_input)); + // CHECK((scan_string("/** /") == json::lexer::token_type::parse_error)); + // + // CHECK((scan_string("/***/") == json::lexer::token_type::end_of_input)); + // } } From f4c4bab600c80746e2fd7e0c03a715d3a9ce0288 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 16 Jun 2020 12:55:36 +0200 Subject: [PATCH 07/49] :sparkles: add option JSON_TestDataDirectory to set path with test data #2189 --- cmake/download_test_data.cmake | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index fe95235f8..7d73c6d35 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -3,15 +3,21 @@ find_package(Git) set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) set(JSON_TEST_DATA_VERSION 2.0.0) -# target to download test data -add_custom_target(download_test_data - COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 - COMMENT "Downloading test data from ${JSON_TEST_DATA_URL} (v${JSON_TEST_DATA_VERSION})" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} -) - -# create a header with the path to the downloaded test data -file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${CMAKE_BINARY_DIR}/json_test_data\"\n") +# if variable is set, use test data from given directory rather than downloading them +if(JSON_TestDataDirectory) + message(STATUS "Using test data in ${JSON_TestDataDirectory}.") + add_custom_target(download_test_data) + file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${JSON_TestDataDirectory}\"\n") +else() + # target to download test data + add_custom_target(download_test_data + COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 + COMMENT "Downloading test data from ${JSON_TEST_DATA_URL} (v${JSON_TEST_DATA_VERSION})" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + # create a header with the path to the downloaded test data + file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${CMAKE_BINARY_DIR}/json_test_data\"\n") +endif() # determine the operating system (for debug and support purposes) find_program(UNAME_COMMAND uname) From 4d96f4cf6aadd3e5d7b0c0f8bb1472644617e1cb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 16 Jun 2020 20:23:01 +0200 Subject: [PATCH 08/49] :wrench: overwork CMake files --- cmake/download_test_data.cmake | 3 +-- test/cmake_fetch_content/project/CMakeLists.txt | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index 7d73c6d35..7d7ff1784 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,5 +1,3 @@ -find_package(Git) - set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) set(JSON_TEST_DATA_VERSION 2.0.0) @@ -9,6 +7,7 @@ if(JSON_TestDataDirectory) add_custom_target(download_test_data) file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${JSON_TestDataDirectory}\"\n") else() + find_package(Git) # target to download test data add_custom_target(download_test_data COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 diff --git a/test/cmake_fetch_content/project/CMakeLists.txt b/test/cmake_fetch_content/project/CMakeLists.txt index 742a112b1..fd8fbdd5f 100644 --- a/test/cmake_fetch_content/project/CMakeLists.txt +++ b/test/cmake_fetch_content/project/CMakeLists.txt @@ -4,9 +4,8 @@ project(DummyImport CXX) include(FetchContent) -FetchContent_Declare(json - GIT_REPOSITORY ${CMAKE_CURRENT_SOURCE_DIR}/../../.. - GIT_TAG HEAD) +get_filename_component(GIT_REPOSITORY_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../.. ABSOLUTE) +FetchContent_Declare(json GIT_REPOSITORY ${GIT_REPOSITORY_DIRECTORY} GIT_TAG HEAD) FetchContent_GetProperties(json) if(NOT json_POPULATED) From 8aaa4013a39786b57964741cd759539112c4fbf2 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 15:05:28 +0800 Subject: [PATCH 09/49] remove overload function, change default_value to rvalue --- include/nlohmann/json.hpp | 36 ++++++-------------------------- single_include/nlohmann/json.hpp | 36 ++++++-------------------------- 2 files changed, 12 insertions(+), 60 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 0deb05833..8f0347eaf 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3770,7 +3770,7 @@ class basic_json template::value and not std::is_same::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + ValueType value(const typename object_t::key_type& key, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -3782,36 +3782,12 @@ class basic_json return *it; } - return default_value; + return std::move(default_value); } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! - @brief overload for a default value of type rvalue - @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const - */ - template>::value - and not std::is_same::value, int>::type = 0> - detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && - { - // only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return std::move(it->template get_ref()); - } - - return std::forward(default_value); - } - - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); - } /*! @brief overload for a default value of type const char* @@ -3819,7 +3795,7 @@ class basic_json */ string_t value(const typename object_t::key_type& key, const char* default_value) const { - return value(key, string_t(default_value)); + return value(key, std::move(string_t(default_value))); } /*! @@ -3867,7 +3843,7 @@ class basic_json */ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const + ValueType value(const json_pointer& ptr, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -3879,7 +3855,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::move(default_value); } } @@ -3893,7 +3869,7 @@ class basic_json JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { - return value(ptr, string_t(default_value)); + return value(ptr, std::move(string_t(default_value))); } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 9fd621c79..0c2cdf3db 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19548,7 +19548,7 @@ class basic_json template::value and not std::is_same::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + ValueType value(const typename object_t::key_type& key, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19560,36 +19560,12 @@ class basic_json return *it; } - return default_value; + return std::move(default_value); } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! - @brief overload for a default value of type rvalue - @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const - */ - template>::value - and not std::is_same::value, int>::type = 0> - detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && - { - // only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return std::move(it->template get_ref()); - } - - return std::forward(default_value); - } - - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); - } /*! @brief overload for a default value of type const char* @@ -19597,7 +19573,7 @@ class basic_json */ string_t value(const typename object_t::key_type& key, const char* default_value) const { - return value(key, string_t(default_value)); + return value(key, std::move(string_t(default_value))); } /*! @@ -19645,7 +19621,7 @@ class basic_json */ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const + ValueType value(const json_pointer& ptr, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19657,7 +19633,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::move(default_value); } } @@ -19671,7 +19647,7 @@ class basic_json JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { - return value(ptr, string_t(default_value)); + return value(ptr, std::move(string_t(default_value))); } /*! From f466919ec29fee46b1dbbb30d241d80b2334cdfe Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 15:07:25 +0800 Subject: [PATCH 10/49] change test cases --- test/src/unit-element_access2.cpp | 41 ++----------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 6df0ca59a..d2a191087 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -148,45 +148,6 @@ TEST_CASE("element access 2") SECTION("access specified element with default value") { - SECTION("move semantics") - { - SECTION("json is rvalue") - { - json j = {{"x", "123"}}; - std::string defval = "default"; - auto val = std::move(j).value("x", defval); - - CHECK(j["x"] == ""); - CHECK(defval == "default"); - CHECK(val == "123"); - } - - SECTION("default is rvalue") - { - json j = {{"x", "123"}}; - std::string defval = "default"; - auto val = std::move(j).value("y", std::move(defval)); - - CHECK(j["x"] == "123"); - CHECK(defval == ""); - CHECK(val == "default"); - } - - SECTION("access on non-object value") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - std::string defval = "default"; - - CHECK_THROWS_AS(std::move(j_nonobject).value("foo", defval), json::type_error&); - CHECK_THROWS_AS(std::move(j_nonobject_const).value("foo", defval), json::type_error&); - CHECK_THROWS_WITH(std::move(j_nonobject).value("foo", defval), - "[json.exception.type_error.306] cannot use value() with array"); - CHECK_THROWS_WITH(std::move(j_nonobject_const).value("foo", defval), - "[json.exception.type_error.306] cannot use value() with array"); - } - } - SECTION("given a key") { SECTION("access existing value") @@ -226,6 +187,7 @@ TEST_CASE("element access 2") CHECK(j.value("_", 12.34) == Approx(12.34)); CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j.value("_", json({10, 100})) == json({10, 100})); + CHECK(j.value("_", std::move("default_value")) == "default_value"); CHECK(j_const.value("_", 2) == 2); CHECK(j_const.value("_", 2u) == 2u); @@ -234,6 +196,7 @@ TEST_CASE("element access 2") CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); + CHECK(j_const.value("_", std::move("default_value")) == "default_value"); } SECTION("access on non-object type") From e86b3fae98854312fc6e59c62e35919a8d8012b1 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 12:35:59 +0200 Subject: [PATCH 11/49] :wrench: add label to tests that require a git checkout --- test/cmake_fetch_content/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cmake_fetch_content/CMakeLists.txt b/test/cmake_fetch_content/CMakeLists.txt index 6d92d1497..0b0d9f65e 100644 --- a/test/cmake_fetch_content/CMakeLists.txt +++ b/test/cmake_fetch_content/CMakeLists.txt @@ -11,8 +11,10 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.11.0") ) set_tests_properties(cmake_fetch_content_configure PROPERTIES FIXTURES_SETUP cmake_fetch_content + LABELS git_required ) set_tests_properties(cmake_fetch_content_build PROPERTIES FIXTURES_REQUIRED cmake_fetch_content + LABELS git_required ) endif() From da8fa3535a71515aeddc68421e63017c1f503d3a Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 20:41:31 +0800 Subject: [PATCH 12/49] drop testcase --- test/src/unit-element_access2.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index d2a191087..e881a9479 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -187,7 +187,6 @@ TEST_CASE("element access 2") CHECK(j.value("_", 12.34) == Approx(12.34)); CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j.value("_", json({10, 100})) == json({10, 100})); - CHECK(j.value("_", std::move("default_value")) == "default_value"); CHECK(j_const.value("_", 2) == 2); CHECK(j_const.value("_", 2u) == 2u); @@ -196,7 +195,6 @@ TEST_CASE("element access 2") CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); - CHECK(j_const.value("_", std::move("default_value")) == "default_value"); } SECTION("access on non-object type") From 4a6c68c7eba1c6c84e90c238b3e1f27dcb096d38 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 20:44:31 +0800 Subject: [PATCH 13/49] drop new blank line --- include/nlohmann/json.hpp | 1 - single_include/nlohmann/json.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8f0347eaf..990fe3cfb 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3788,7 +3788,6 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0c2cdf3db..8a79c4798 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19566,7 +19566,6 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const From e9bfcf72550c7ba99b5cdd5c0b1e5038c1bcc4ae Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 14:59:47 +0200 Subject: [PATCH 14/49] :zap: improve comment parsing --- include/nlohmann/detail/input/lexer.hpp | 72 +++++++++++-------------- single_include/nlohmann/json.hpp | 72 +++++++++++-------------- 2 files changed, 64 insertions(+), 80 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 00af0ce21..eab64f406 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -835,62 +835,54 @@ class lexer : public lexer_base */ bool scan_comment() { - // remember character after '/' to distinguish comment types - const auto comment_char = get(); - - // expect // or /* to start a comment - if (comment_char != '/' and comment_char != '*') + switch (get()) { - return false; - } - - while (true) - { - switch (get()) + case '/': { - // EOF inside a /* comment is an error, in // it is OK - case std::char_traits::eof(): - case '\0': + while (true) { - return comment_char == '/'; - } - - // a newline ends the // comment - case '\n': - case '\r': - { - if (comment_char == '/') + switch (get()) { - return true; + case '\n': + case '\r': + return true; + + default: + break; } - break; } + } - // */ ends the /* comment - case '*': + case '*': + { + while (true) { - if (comment_char == '*') + switch (get()) { - switch (get()) - { - case '/': - { - return true; - } + case std::char_traits::eof(): + case '\0': + return false; - default: + case '*': + { + switch (get()) { - unget(); - break; + case '/': + return true; + + default: + { + unget(); + break; + } } } } - break; } - - default: - break; } + + default: + return false; } } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 82435fa7a..bdd97a145 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8902,62 +8902,54 @@ class lexer : public lexer_base */ bool scan_comment() { - // remember character after '/' to distinguish comment types - const auto comment_char = get(); - - // expect // or /* to start a comment - if (comment_char != '/' and comment_char != '*') + switch (get()) { - return false; - } - - while (true) - { - switch (get()) + case '/': { - // EOF inside a /* comment is an error, in // it is OK - case std::char_traits::eof(): - case '\0': + while (true) { - return comment_char == '/'; - } - - // a newline ends the // comment - case '\n': - case '\r': - { - if (comment_char == '/') + switch (get()) { - return true; + case '\n': + case '\r': + return true; + + default: + break; } - break; } + } - // */ ends the /* comment - case '*': + case '*': + { + while (true) { - if (comment_char == '*') + switch (get()) { - switch (get()) - { - case '/': - { - return true; - } + case std::char_traits::eof(): + case '\0': + return false; - default: + case '*': + { + switch (get()) { - unget(); - break; + case '/': + return true; + + default: + { + unget(); + break; + } } } } - break; } - - default: - break; } + + default: + return false; } } From 88a37010d6cfa808ddfa9559a15285b8291ad187 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 21:14:23 +0200 Subject: [PATCH 15/49] :bug: serialize 32-bit floating-point numbers as float 32 in MessagePack (0xCA) #2196 --- .../features/binary_formats/messagepack.md | 7 ++-- .../nlohmann/detail/output/binary_writer.hpp | 14 ++++++-- include/nlohmann/json.hpp | 6 ++-- single_include/nlohmann/json.hpp | 20 +++++++---- test/src/unit-msgpack.cpp | 34 +++++++++++++++++++ 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/doc/mkdocs/docs/features/binary_formats/messagepack.md b/doc/mkdocs/docs/features/binary_formats/messagepack.md index ed0610569..3e041bb70 100644 --- a/doc/mkdocs/docs/features/binary_formats/messagepack.md +++ b/doc/mkdocs/docs/features/binary_formats/messagepack.md @@ -31,7 +31,8 @@ number_unsigned | 128..255 | uint 8 | 0xCC number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF -number_float | *any value* | float 64 | 0xCB +number_float | *any value representable by a float* | float 32 | 0xCA +number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -61,10 +62,6 @@ binary | *size*: 65536..4294967295 | bin 32 | 0xC6 - arrays with more than 4294967295 elements - objects with more than 4294967295 elements -!!! info "Unused MessagePack types" - - The following MessagePack types are not used in the conversion: float 32 (0xCA) - !!! info "NaN/infinity handling" If NaN or Infinity are stored inside a JSON number, they are serialized properly. function which serializes NaN or Infinity to `null`. diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 269df0d55..f32211911 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -504,8 +504,18 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and + static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) + { + oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); + write_number(static_cast(j.m_value.number_float)); + } + else + { + oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); + write_number(j.m_value.number_float); + } break; } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 409a6e799..790ecd4b2 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -7040,7 +7040,8 @@ class basic_json number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -7064,9 +7065,6 @@ class basic_json - arrays with more than 4294967295 elements - objects with more than 4294967295 elements - @note The following MessagePack types are not used in the conversion: - - float 32 (0xCA) - @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543..85fffc7c7 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12714,8 +12714,18 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and + static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) + { + oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); + write_number(static_cast(j.m_value.number_float)); + } + else + { + oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); + write_number(j.m_value.number_float); + } break; } @@ -22821,7 +22831,8 @@ class basic_json number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -22845,9 +22856,6 @@ class basic_json - arrays with more than 4294967295 elements - objects with more than 4294967295 elements - @note The following MessagePack types are not used in the conversion: - - float 32 (0xCA) - @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 2744a67bf..5baecf027 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -783,6 +783,40 @@ TEST_CASE("MessagePack") CHECK(json::from_msgpack(result) == v); CHECK(json::from_msgpack(result, true, false) == j); } + + SECTION("1.0") + { + double v = 1.0; + json j = v; + std::vector expected = + { + 0xca, 0x3f, 0x80, 0x00, 0x00 + }; + const auto result = json::to_msgpack(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_msgpack(result) == j); + CHECK(json::from_msgpack(result) == v); + CHECK(json::from_msgpack(result, true, false) == j); + } + + SECTION("128.128") + { + double v = 128.1280059814453125; + json j = v; + std::vector expected = + { + 0xca, 0x43, 0x00, 0x20, 0xc5 + }; + const auto result = json::to_msgpack(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_msgpack(result) == j); + CHECK(json::from_msgpack(result) == v); + CHECK(json::from_msgpack(result, true, false) == j); + } } } From 74520d8bb0aa62374e5c5465b5b0f3b43d75d956 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 22:03:14 +0200 Subject: [PATCH 16/49] :construction: extend API --- include/nlohmann/detail/input/lexer.hpp | 8 +++ include/nlohmann/detail/input/parser.hpp | 7 ++- include/nlohmann/json.hpp | 63 ++++++++++++------- single_include/nlohmann/json.hpp | 78 +++++++++++++++++------- test/src/unit-class_lexer.cpp | 58 ++++++++++++------ 5 files changed, 148 insertions(+), 66 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index eab64f406..d5e243e63 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -837,6 +837,7 @@ class lexer : public lexer_base { switch (get()) { + // single-line comments skip input until a newline or EOF is read case '/': { while (true) @@ -845,6 +846,8 @@ class lexer : public lexer_base { case '\n': case '\r': + case std::char_traits::eof(): + case '\0': return true; default: @@ -853,6 +856,7 @@ class lexer : public lexer_base } } + // multi-line comments skip input until */ is read case '*': { while (true) @@ -877,10 +881,14 @@ class lexer : public lexer_base } } } + + default: + break; } } } + // unexpected character after reading '/' default: return false; } diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 0546b88cb..c79b492aa 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -63,8 +63,11 @@ class parser /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, const parser_callback_t cb = nullptr, - const bool allow_exceptions_ = true) - : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) { // read first token get_token(); diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 409a6e799..8698a9bb8 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -196,10 +196,12 @@ class basic_json static ::nlohmann::detail::parser parser( InputAdapterType adapter, detail::parser_callback_tcb = nullptr, - bool allow_exceptions = true + const bool allow_exceptions = true, + const bool ignore_comments = false ) { - return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; @@ -6563,6 +6565,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6591,16 +6595,18 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - @since version 2.0.3 (contiguous containers) + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -6617,6 +6623,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6632,10 +6640,11 @@ class basic_json static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -6643,10 +6652,11 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(i.get(), cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -6666,6 +6676,8 @@ class basic_json iterators. @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return Whether the input read from @a i is valid JSON. @@ -6678,22 +6690,25 @@ class basic_json from a string.,accept__string} */ template - static bool accept(InputType&& i) + static bool accept(InputType&& i, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::forward(i))).accept(true); + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); } template - static bool accept(IteratorType first, IteratorType last) + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) - static bool accept(detail::span_input_adapter&& i) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) { - return parser(i.get()).accept(true); + return parser(i.get(), nullptr, false, ignore_comments).accept(true); } /*! @@ -6713,6 +6728,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default); only applieds to + the JSON file format. @return return value of the last processed SAX event @@ -6737,11 +6755,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -6749,11 +6768,12 @@ class basic_json JSON_HEDLEY_NON_NULL(3) static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -6762,11 +6782,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bdd97a145..099fdd8ea 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8904,6 +8904,7 @@ class lexer : public lexer_base { switch (get()) { + // single-line comments skip input until a newline or EOF is read case '/': { while (true) @@ -8912,6 +8913,8 @@ class lexer : public lexer_base { case '\n': case '\r': + case std::char_traits::eof(): + case '\0': return true; default: @@ -8920,6 +8923,7 @@ class lexer : public lexer_base } } + // multi-line comments skip input until */ is read case '*': { while (true) @@ -8944,10 +8948,14 @@ class lexer : public lexer_base } } } + + default: + break; } } } + // unexpected character after reading '/' default: return false; } @@ -9742,8 +9750,11 @@ class parser /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, const parser_callback_t cb = nullptr, - const bool allow_exceptions_ = true) - : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) { // read first token get_token(); @@ -16051,10 +16062,12 @@ class basic_json static ::nlohmann::detail::parser parser( InputAdapterType adapter, detail::parser_callback_tcb = nullptr, - bool allow_exceptions = true + const bool allow_exceptions = true, + const bool ignore_comments = false ) { - return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; @@ -22418,6 +22431,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22446,16 +22461,18 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - @since version 2.0.3 (contiguous containers) + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22472,6 +22489,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22487,10 +22506,11 @@ class basic_json static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22498,10 +22518,11 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(i.get(), cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22521,6 +22542,8 @@ class basic_json iterators. @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return Whether the input read from @a i is valid JSON. @@ -22533,22 +22556,25 @@ class basic_json from a string.,accept__string} */ template - static bool accept(InputType&& i) + static bool accept(InputType&& i, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::forward(i))).accept(true); + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); } template - static bool accept(IteratorType first, IteratorType last) + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) - static bool accept(detail::span_input_adapter&& i) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) { - return parser(i.get()).accept(true); + return parser(i.get(), nullptr, false, ignore_comments).accept(true); } /*! @@ -22568,6 +22594,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default); only applieds to + the JSON file format. @return return value of the last processed SAX event @@ -22592,11 +22621,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -22604,11 +22634,12 @@ class basic_json JSON_HEDLEY_NON_NULL(3) static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -22617,11 +22648,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index c4423e602..aee4703fc 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -37,11 +37,11 @@ using nlohmann::json; namespace { // shortcut to scan a string literal -json::lexer::token_type scan_string(const char* s); -json::lexer::token_type scan_string(const char* s) +json::lexer::token_type scan_string(const char* s, const bool ignore_comments = false); +json::lexer::token_type scan_string(const char* s, const bool ignore_comments) { auto ia = nlohmann::detail::input_adapter(s); - return nlohmann::detail::lexer(std::move(ia)).scan(); + return nlohmann::detail::lexer(std::move(ia), ignore_comments).scan(); } } @@ -163,9 +163,6 @@ TEST_CASE("lexer class") break; } - // case ('/'): - // break; - // anything else is not expected default: { @@ -185,18 +182,39 @@ TEST_CASE("lexer class") CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string)); } - // SECTION("ignore comments") - // { - // CHECK((scan_string("/") == json::lexer::token_type::parse_error)); - // - // CHECK((scan_string("/!") == json::lexer::token_type::parse_error)); - // CHECK((scan_string("/*") == json::lexer::token_type::parse_error)); - // CHECK((scan_string("/**") == json::lexer::token_type::parse_error)); - // - // CHECK((scan_string("//") == json::lexer::token_type::end_of_input)); - // CHECK((scan_string("/**/") == json::lexer::token_type::end_of_input)); - // CHECK((scan_string("/** /") == json::lexer::token_type::parse_error)); - // - // CHECK((scan_string("/***/") == json::lexer::token_type::end_of_input)); - // } + SECTION("fail on comments") + { + CHECK((scan_string("/", false) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("//", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error)); + } + + SECTION("ignore comments") + { + CHECK((scan_string("/", true) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input)); + } } From cd115cbc33d074e231d3a703c8120b72223cb1e4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 18 Jun 2020 12:50:32 +0200 Subject: [PATCH 17/49] :white_check_mark: update test suite --- cmake/download_test_data.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index fe95235f8..b023d8b45 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,7 +1,7 @@ find_package(Git) set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) -set(JSON_TEST_DATA_VERSION 2.0.0) +set(JSON_TEST_DATA_VERSION 3.0.0) # target to download test data add_custom_target(download_test_data From 0585ecc56b210e045e639c41474cc72c475c8b76 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 19 Jun 2020 13:10:35 +0200 Subject: [PATCH 18/49] :white_check_mark: add tests for comment skipping --- include/nlohmann/detail/input/lexer.hpp | 8 ++++- single_include/nlohmann/json.hpp | 8 ++++- test/src/unit-class_parser.cpp | 45 ++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index d5e243e63..9dba2972c 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1507,7 +1507,13 @@ scan_number_done: error_message = "invalid comment"; return token_type::parse_error; } - get(); + + // skip following whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); } switch (current) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 099fdd8ea..c341d40d2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -9574,7 +9574,13 @@ scan_number_done: error_message = "invalid comment"; return token_type::parse_error; } - get(); + + // skip following whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); } switch (current) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 1912094aa..de5c96385 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -224,6 +224,7 @@ class SaxCountdown : public nlohmann::json::json_sax_t json parser_helper(const std::string& s); bool accept_helper(const std::string& s); +void comments_helper(const std::string& s); json parser_helper(const std::string& s) { @@ -241,6 +242,8 @@ json parser_helper(const std::string& s) json::sax_parse(s, &sdp); CHECK(j_sax == j); + comments_helper(s); + return j; } @@ -275,11 +278,51 @@ bool accept_helper(const std::string& s) // 6. check if this approach came to the same result CHECK(ok_noexcept == ok_noexcept_cb); - // 7. return result + // 7. check if comments are properly ignored + if (ok_accept) + { + comments_helper(s); + } + + // 8. return result return ok_accept; } + +void comments_helper(const std::string& s) +{ + json _; + + // parse/accept with default parser + CHECK_NOTHROW(_ = json::parse(s)); + CHECK(json::accept(s)); + + // parse/accept while skipping comments + CHECK_NOTHROW(_ = json::parse(s, nullptr, false, true)); + CHECK(json::accept(s, true)); + + std::vector json_with_comments; + + // start with a comment + json_with_comments.push_back(std::string("// this is a comment\n") + s); + json_with_comments.push_back(std::string("/* this is a comment */") + s); + // end with a comment + json_with_comments.push_back(s + "// this is a comment"); + json_with_comments.push_back(s + "/* this is a comment */"); + + // check all strings + for (const auto& json_with_comment : json_with_comments) + { + CAPTURE(json_with_comment) + CHECK_THROWS_AS(_ = json::parse(json_with_comment), json::parse_error); + CHECK(not json::accept(json_with_comment)); + + CHECK_NOTHROW(_ = json::parse(json_with_comment, nullptr, true, true)); + CHECK(json::accept(json_with_comment, true)); + } } +} // namespace + TEST_CASE("parser class") { SECTION("parse") From b64002bbca497e79f56466ced8f99f90ca9cf347 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 19 Jun 2020 13:24:08 +0200 Subject: [PATCH 19/49] :recycle: extract common code to function --- .../nlohmann/detail/output/binary_writer.hpp | 47 +++++++++---------- single_include/nlohmann/json.hpp | 47 +++++++++---------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index f32211911..7289da3dc 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -28,6 +28,7 @@ class binary_writer { using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; public: /*! @@ -194,18 +195,7 @@ class binary_writer } else { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_cbor_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } @@ -504,18 +494,7 @@ class binary_writer case value_t::number_float: { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -1528,6 +1507,26 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) and + static_cast(n) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + public: // The following to_char_type functions are implement the conversion // between uint8_t and CharType. In case CharType is not unsigned, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 85fffc7c7..33d0c3577 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12238,6 +12238,7 @@ class binary_writer { using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; public: /*! @@ -12404,18 +12405,7 @@ class binary_writer } else { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_cbor_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } @@ -12714,18 +12704,7 @@ class binary_writer case value_t::number_float: { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -13738,6 +13717,26 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) and + static_cast(n) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + public: // The following to_char_type functions are implement the conversion // between uint8_t and CharType. In case CharType is not unsigned, From 24992003d99fefdd9fb5a605f0eb58a05a91985b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 20 Jun 2020 09:55:11 +0200 Subject: [PATCH 20/49] :memo: add notes from #2189 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d7492da34..42d057a35 100644 --- a/README.md +++ b/README.md @@ -1557,4 +1557,6 @@ $ cmake --build . $ ctest --output-on-failure ``` -For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). +Note that during the `ctest` stage, several JSON test files are downloaded from an [external repository](https://github.com/nlohmann/json_test_data). If policies forbid downloading artifacts during testing, you can download the files yourself and pass the directory with the test files via `-DJSON_TestDataDirectory=path` to CMake. Then, no Internet connectivity is required. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. + +In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure`. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. From 0fe9f23254c98172c71a08ca5a1f75d4e6974811 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 20 Jun 2020 14:11:37 +0200 Subject: [PATCH 21/49] :sparkles: add macros from #2175 --- include/nlohmann/detail/macro_scope.hpp | 41 ++++++++ single_include/nlohmann/json.hpp | 41 ++++++++ test/CMakeLists.txt | 1 + test/src/unit-udt_macro.cpp | 126 ++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 test/src/unit-udt_macro.cpp diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 25e6b76eb..92bc6370d 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -120,3 +120,44 @@ basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1,_2,_3,_4,_5,_6, _7, _8, _9, _10, _11, NAME,...) NAME + +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, NLOHMANN_JSON_PASTE9, NLOHMANN_JSON_PASTE8, NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, NLOHMANN_JSON_PASTE5, NLOHMANN_JSON_PASTE4, NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) + +#define NLOHMANN_JSON_TO(v1) j[#v1] = t.v1; +#define NLOHMANN_JSON_FROM(v1) j.at(#v1).get_to(t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543..0c34d28ea 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2149,6 +2149,47 @@ JSON_HEDLEY_DIAGNOSTIC_POP NumberIntegerType, NumberUnsignedType, NumberFloatType, \ AllocatorType, JSONSerializer, BinaryType> +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1,_2,_3,_4,_5,_6, _7, _8, _9, _10, _11, NAME,...) NAME + +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, NLOHMANN_JSON_PASTE9, NLOHMANN_JSON_PASTE8, NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, NLOHMANN_JSON_PASTE5, NLOHMANN_JSON_PASTE4, NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) + +#define NLOHMANN_JSON_TO(v1) j[#v1] = t.v1; +#define NLOHMANN_JSON_FROM(v1) j.at(#v1).get_to(t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + namespace nlohmann { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fdf5ac5b3..8434f78d7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -132,6 +132,7 @@ set(files src/unit-to_chars.cpp src/unit-ubjson.cpp src/unit-udt.cpp + src/unit-udt_macro.cpp src/unit-unicode.cpp src/unit-user_defined_input.cpp src/unit-wstring.cpp) diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp new file mode 100644 index 000000000..ab2d32451 --- /dev/null +++ b/test/src/unit-udt_macro.cpp @@ -0,0 +1,126 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.8.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +#include +using nlohmann::json; + +#include + +namespace persons +{ +class person_with_private_data +{ + private: + std::string name; + int age = 0; + + public: + bool operator==(const person_with_private_data& rhs) const + { + return std::tie(name, age) == std::tie(rhs.name, rhs.age); + } + + person_with_private_data() = default; + person_with_private_data(std::string name, int age) + : name(std::move(name)) + , age(age) + {} + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name); +}; + +class person_without_private_data_1 +{ + public: + std::string name; + int age = 0; + + bool operator==(const person_without_private_data_1& rhs) const + { + return std::tie(name, age) == std::tie(rhs.name, rhs.age); + } + + person_without_private_data_1() = default; + person_without_private_data_1(std::string name, int age) + : name(std::move(name)) + , age(age) + {} + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name); +}; + +class person_without_private_data_2 +{ + public: + std::string name; + int age = 0; + + bool operator==(const person_without_private_data_2& rhs) const + { + return std::tie(name, age) == std::tie(rhs.name, rhs.age); + } + + person_without_private_data_2() = default; + person_without_private_data_2(std::string name, int age) + : name(std::move(name)) + , age(age) + {} +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name); +} // namespace persons + +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T, + persons::person_with_private_data, + persons::person_without_private_data_1, + persons::person_without_private_data_2) +{ + SECTION("person") + { + // serialization + T p1("Erik", 1); + CHECK(json(p1).dump() == "{\"age\":1,\"name\":\"Erik\"}"); + + // deserialization + T p2 = json(p1); + CHECK(p2 == p1); + + // roundtrip + CHECK(T(json(p1)) == p1); + CHECK(json(T(json(p1))) == json(p1)); + + // check exception in case of missing field + json j = json(p1); + j.erase("age"); + T p3; + CHECK_THROWS_WITH_AS(p3 = json(j), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range); + } +} From f0e73163f242cd631848d007a3765fe6b94c5bcd Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sat, 20 Jun 2020 15:25:54 +0200 Subject: [PATCH 22/49] json_pointer.hpp: Mention more exception in documentation Forgotten in dcd3a6c6 (move the catch of std::invalid_argument into array_index(), 2020-03-23). --- include/nlohmann/detail/json_pointer.hpp | 2 ++ single_include/nlohmann/json.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 421c5ec8f..18b66e234 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -325,6 +325,8 @@ class json_pointer @return integer representation of @a s + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer */ static int array_index(const std::string& s) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 33d0c3577..22fc0e330 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11357,6 +11357,8 @@ class json_pointer @return integer representation of @a s + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer */ static int array_index(const std::string& s) From e22ce4506564f3630e4cd63dc0795115df90be71 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 21 Jun 2020 13:28:00 +0200 Subject: [PATCH 23/49] :children_crossing: improve diagnostics --- include/nlohmann/detail/input/lexer.hpp | 7 ++++++- single_include/nlohmann/json.hpp | 7 ++++++- test/src/unit-class_lexer.cpp | 25 +++++++++++++++++++++++++ test/src/unit-class_parser.cpp | 6 ++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 9dba2972c..580b1c225 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -865,7 +865,10 @@ class lexer : public lexer_base { case std::char_traits::eof(): case '\0': + { + error_message = "invalid comment; missing closing '*/'"; return false; + } case '*': { @@ -890,7 +893,10 @@ class lexer : public lexer_base // unexpected character after reading '/' default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; return false; + } } } @@ -1504,7 +1510,6 @@ scan_number_done: { if (not scan_comment()) { - error_message = "invalid comment"; return token_type::parse_error; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f4fe86429..cdc3de095 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8932,7 +8932,10 @@ class lexer : public lexer_base { case std::char_traits::eof(): case '\0': + { + error_message = "invalid comment; missing closing '*/'"; return false; + } case '*': { @@ -8957,7 +8960,10 @@ class lexer : public lexer_base // unexpected character after reading '/' default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; return false; + } } } @@ -9571,7 +9577,6 @@ scan_number_done: { if (not scan_comment()) { - error_message = "invalid comment"; return token_type::parse_error; } diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index aee4703fc..d8304ccf1 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -45,6 +45,15 @@ json::lexer::token_type scan_string(const char* s, const bool ignore_comments) } } +std::string get_error_message(const char* s, const bool ignore_comments = false); +std::string get_error_message(const char* s, const bool ignore_comments) +{ + auto ia = nlohmann::detail::input_adapter(s); + auto lexer = nlohmann::detail::lexer(std::move(ia), ignore_comments); + lexer.scan(); + return lexer.get_error_message(); +} + TEST_CASE("lexer class") { SECTION("scan") @@ -185,32 +194,48 @@ TEST_CASE("lexer class") SECTION("fail on comments") { CHECK((scan_string("/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/", false) == "invalid literal"); CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/!", false) == "invalid literal"); CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*", false) == "invalid literal"); CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/**", false) == "invalid literal"); CHECK((scan_string("//", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("//", false) == "invalid literal"); CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/**/", false) == "invalid literal"); CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/** /", false) == "invalid literal"); CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/***/", false) == "invalid literal"); CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/* true */", false) == "invalid literal"); CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*/**/", false) == "invalid literal"); CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*/* */", false) == "invalid literal"); } SECTION("ignore comments") { CHECK((scan_string("/", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'"); CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'"); CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'"); CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'"); CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input)); CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input)); CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'"); CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input)); CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input)); diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index de5c96385..da16ffcab 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1877,4 +1877,10 @@ TEST_CASE("parser class") } } } + + SECTION("error messages for comments") + { + CHECK_THROWS_WITH_AS(json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error); + CHECK_THROWS_WITH_AS(json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*'", json::parse_error); + } } From 65e8ee985ae31e73223e6f722c26d6a1fb0563c8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 22 Jun 2020 08:59:03 +0200 Subject: [PATCH 24/49] :hammer: clean up --- README.md | 8 ++--- include/nlohmann/detail/input/lexer.hpp | 21 +++++++------ include/nlohmann/json.hpp | 21 +++++++------ single_include/nlohmann/json.hpp | 42 ++++++++++++++----------- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 42d057a35..0699bdc30 100644 --- a/README.md +++ b/README.md @@ -1508,7 +1508,7 @@ The library supports **Unicode input** as follows: ### Comments in JSON -This library does not support comments. It does so for three reasons: +This library does not support comments by default. It does so for three reasons: 1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. 2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: @@ -1519,11 +1519,7 @@ This library does not support comments. It does so for three reasons: 3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this. -This library will not support comments in the future. If you wish to use comments, I see three options: - -1. Strip comments before using this library. -2. Use a different JSON library with comment support. -3. Use a format that natively supports comments (e.g., YAML or JSON5). +However, you can pass set parameter `ignore_comments` to true in the `parse` function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. ### Order of object keys diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 580b1c225..8042f3c4e 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1489,6 +1489,15 @@ scan_number_done: return true; } + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + } + token_type scan() { // initially, skip the BOM @@ -1499,11 +1508,7 @@ scan_number_done: } // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); // ignore comments if (ignore_comments and current == '/') @@ -1514,11 +1519,7 @@ scan_number_done: } // skip following whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); } switch (current) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index f4fa590ca..cc7e3fbc2 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -6565,8 +6565,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6623,8 +6624,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6676,8 +6678,9 @@ class basic_json iterators. @param[in] i input to read from - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return Whether the input read from @a i is valid JSON. @@ -6728,9 +6731,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default); only applieds to - the JSON file format. + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. @return return value of the last processed SAX event diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cdc3de095..683e2d560 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -9556,6 +9556,15 @@ scan_number_done: return true; } + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + } + token_type scan() { // initially, skip the BOM @@ -9566,11 +9575,7 @@ scan_number_done: } // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); // ignore comments if (ignore_comments and current == '/') @@ -9581,11 +9586,7 @@ scan_number_done: } // skip following whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); } switch (current) @@ -22451,8 +22452,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22509,8 +22511,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22562,8 +22565,9 @@ class basic_json iterators. @param[in] i input to read from - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return Whether the input read from @a i is valid JSON. @@ -22614,9 +22618,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default); only applieds to - the JSON file format. + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. @return return value of the last processed SAX event From ecbb2756fd7629a8dba64be581e19488d0be3061 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sat, 20 Jun 2020 15:36:28 +0200 Subject: [PATCH 25/49] json_pointer::array_index: Use unsigned values for the array index when parsing The current code uses std::stoi to convert the input string to an int array_index. This limits the maximum addressable array size to ~2GB on most platforms. But all callers immediately convert the result of array_index to BasicJsonType::size_type. So let's parse it as unsigned long long, which allows us to have as big arrays as available memory. And also makes the call sites nicer to read. One complication arises on platforms where size_type is smaller than unsigned long long. We need to bail out on these if the parsed array index does not fit into size_type. --- include/nlohmann/detail/json_pointer.hpp | 33 +++++++++++++-------- include/nlohmann/json.hpp | 4 +-- single_include/nlohmann/json.hpp | 37 +++++++++++++++--------- test/src/unit-json_pointer.cpp | 29 +++++++++++++++---- 4 files changed, 69 insertions(+), 34 deletions(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 18b66e234..562802920 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -3,6 +3,7 @@ #include // all_of #include // assert #include // isdigit +#include // max #include // accumulate #include // string #include // move @@ -328,9 +329,12 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type */ - static int array_index(const std::string& s) + static typename BasicJsonType::size_type array_index(const std::string& s) { + using size_type = typename BasicJsonType::size_type; + // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) { @@ -346,10 +350,10 @@ class json_pointer } std::size_t processed_chars = 0; - int res = 0; + unsigned long long res = 0; JSON_TRY { - res = std::stoi(s, &processed_chars); + res = std::stoull(s, &processed_chars); } JSON_CATCH(std::out_of_range&) { @@ -362,7 +366,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - return res; + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + } + + return static_cast(res); } json_pointer top() const @@ -421,7 +432,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(array_index(reference_token))); + result = &result->operator[](array_index(reference_token)); break; } @@ -499,8 +510,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -544,7 +554,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -594,8 +604,7 @@ class json_pointer } // use unchecked array access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -638,7 +647,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -702,7 +711,7 @@ class json_pointer } } - const auto idx = static_cast(array_index(reference_token)); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 790ecd4b2..048307b3a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -8179,7 +8179,7 @@ class basic_json else { const auto idx = json_pointer::array_index(last_path); - if (JSON_HEDLEY_UNLIKELY(static_cast(idx) > parent.size())) + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); @@ -8222,7 +8222,7 @@ class basic_json else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast(json_pointer::array_index(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 22fc0e330..b919d2318 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11032,6 +11032,7 @@ class json_reverse_iterator : public std::reverse_iterator #include // all_of #include // assert #include // isdigit +#include // max #include // accumulate #include // string #include // move @@ -11360,9 +11361,12 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type */ - static int array_index(const std::string& s) + static typename BasicJsonType::size_type array_index(const std::string& s) { + using size_type = typename BasicJsonType::size_type; + // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) { @@ -11378,10 +11382,10 @@ class json_pointer } std::size_t processed_chars = 0; - int res = 0; + unsigned long long res = 0; JSON_TRY { - res = std::stoi(s, &processed_chars); + res = std::stoull(s, &processed_chars); } JSON_CATCH(std::out_of_range&) { @@ -11394,7 +11398,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - return res; + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + } + + return static_cast(res); } json_pointer top() const @@ -11453,7 +11464,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(array_index(reference_token))); + result = &result->operator[](array_index(reference_token)); break; } @@ -11531,8 +11542,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -11576,7 +11586,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -11626,8 +11636,7 @@ class json_pointer } // use unchecked array access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -11670,7 +11679,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -11734,7 +11743,7 @@ class json_pointer } } - const auto idx = static_cast(array_index(reference_token)); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range @@ -23971,7 +23980,7 @@ class basic_json else { const auto idx = json_pointer::array_index(last_path); - if (JSON_HEDLEY_UNLIKELY(static_cast(idx) > parent.size())) + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); @@ -24014,7 +24023,7 @@ class basic_json else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast(json_pointer::array_index(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index ca11efa42..89a41d76b 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -348,12 +348,29 @@ TEST_CASE("JSON pointers") CHECK_THROWS_WITH(j_const["/1+1"_json_pointer] == 1, "[json.exception.out_of_range.404] unresolved reference token '1+1'"); - CHECK_THROWS_AS(j["/111111111111111111111111"_json_pointer] = 1, json::out_of_range&); - CHECK_THROWS_WITH(j["/111111111111111111111111"_json_pointer] = 1, - "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'"); - CHECK_THROWS_AS(j_const["/111111111111111111111111"_json_pointer] == 1, json::out_of_range&); - CHECK_THROWS_WITH(j_const["/111111111111111111111111"_json_pointer] == 1, - "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'"); + { + auto too_large_index = std::to_string((std::numeric_limits::max)()) + "1"; + json::json_pointer jp(std::string("/") + too_large_index); + std::string throw_msg = std::string("[json.exception.out_of_range.404] unresolved reference token '") + too_large_index + "'"; + + CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&); + CHECK_THROWS_WITH(j[jp] = 1, throw_msg.c_str()); + CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&); + CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str()); + } + + if (sizeof(typename json::size_type) < sizeof(unsigned long long)) + { + auto size_type_max_uul = static_cast((std::numeric_limits::max)()); + auto too_large_index = std::to_string(size_type_max_uul); + json::json_pointer jp(std::string("/") + too_large_index); + std::string throw_msg = std::string("[json.exception.out_of_range.410] array index ") + too_large_index + " exceeds size_type"; + + CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&); + CHECK_THROWS_WITH(j[jp] = 1, throw_msg.c_str()); + CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&); + CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str()); + } CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&); CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1, From aeef50709e6a49f5807cdf787473ca8adecbe23c Mon Sep 17 00:00:00 2001 From: chenguoping Date: Mon, 22 Jun 2020 20:17:56 +0800 Subject: [PATCH 26/49] to allow for ADL in int_to_string() function --- include/nlohmann/detail/iterators/iteration_proxy.hpp | 4 +++- single_include/nlohmann/json.hpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index 48927b018..d19be4ddd 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -16,7 +16,9 @@ template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { - target = std::to_string(value); + // For ADL + using std::to_string; + target = to_string(value); } template class iteration_proxy_value { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 69a4ec092..58b12a7df 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3654,7 +3654,9 @@ template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { - target = std::to_string(value); + // For ADL + using std::to_string; + target = to_string(value); } template class iteration_proxy_value { From 8b3d2399a487a3d5c52a09d258d9df83e7a6066e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 22 Jun 2020 22:32:21 +0200 Subject: [PATCH 27/49] :rotating_light: remove warnings --- .clang-tidy | 27 +- .../nlohmann/detail/input/binary_reader.hpp | 166 ++++++------ .../nlohmann/detail/input/input_adapters.hpp | 4 +- include/nlohmann/detail/input/json_sax.hpp | 20 +- include/nlohmann/detail/input/lexer.hpp | 2 +- .../detail/iterators/internal_iterator.hpp | 2 - include/nlohmann/detail/json_ref.hpp | 19 +- .../nlohmann/detail/output/binary_writer.hpp | 26 +- include/nlohmann/detail/output/serializer.hpp | 8 +- single_include/nlohmann/json.hpp | 247 +++++++++--------- 10 files changed, 250 insertions(+), 271 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index feee81945..046d84f87 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,23 +1,20 @@ -Checks: '-*, - bugprone-*, - cert-*, - clang-analyzer-*, - google-*, - -google-runtime-references, +Checks: '*, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-macro-usage, + -fuchsia-default-arguments-calls, + -fuchsia-default-arguments-declarations, + -fuchsia-overloaded-operator, -google-explicit-constructor, - hicpp-*, + -google-runtime-references, + -hicpp-avoid-goto, + -hicpp-explicit-conversions, -hicpp-no-array-decay, -hicpp-uppercase-literal-suffix, - -hicpp-explicit-conversions, - misc-*, - -misc-non-private-member-variables-in-classes, - llvm-*, -llvm-header-guard, - modernize-*, + -llvm-include-order, + -misc-non-private-member-variables-in-classes, -modernize-use-trailing-return-type, - performance-*, - portability-*, - readability-*, -readability-magic-numbers, -readability-uppercase-literal-suffix' diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 93667e8f6..a27a15387 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -145,7 +145,7 @@ class binary_reader */ bool parse_bson_internal() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) @@ -184,8 +184,6 @@ class binary_reader } *out++ = static_cast(current); } - - return true; } /*! @@ -230,7 +228,7 @@ class binary_reader } // All BSON binary values have a subtype - std::uint8_t subtype; + std::uint8_t subtype{}; get_number(input_format_t::bson, subtype); result.set_subtype(subtype); @@ -254,13 +252,13 @@ class binary_reader { case 0x01: // double { - double number; + double number{}; return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); } case 0x02: // string { - std::int32_t len; + std::int32_t len{}; string_t value; return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); } @@ -277,7 +275,7 @@ class binary_reader case 0x05: // binary { - std::int32_t len; + std::int32_t len{}; binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -294,13 +292,13 @@ class binary_reader case 0x10: // int32 { - std::int32_t value; + std::int32_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } case 0x12: // int64 { - std::int64_t value; + std::int64_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } @@ -365,7 +363,7 @@ class binary_reader */ bool parse_bson_array() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) @@ -429,25 +427,25 @@ class binary_reader case 0x18: // Unsigned integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x19: // Unsigned integer (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1A: // Unsigned integer (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1B: // Unsigned integer (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } @@ -480,25 +478,25 @@ class binary_reader case 0x38: // Negative integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - static_cast(number)); } @@ -602,25 +600,25 @@ class binary_reader case 0x98: // array (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x99: // array (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9A: // array (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9B: // array (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } @@ -656,25 +654,25 @@ class binary_reader case 0xB8: // map (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xB9: // map (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBA: // map (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBB: // map (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } @@ -740,13 +738,13 @@ class binary_reader case 0xFA: // Single-Precision Float (four-byte IEEE 754) { - float number; + float number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { - double number; + double number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } @@ -809,25 +807,25 @@ class binary_reader case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } @@ -904,28 +902,28 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } @@ -1290,85 +1288,85 @@ class binary_reader case 0xCA: // float 32 { - float number; + float number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCB: // float 64 { - double number; + double number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCC: // uint 8 { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCD: // uint 16 { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCE: // uint 32 { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCF: // uint 64 { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xD0: // int 8 { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD1: // int 16 { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD2: // int 32 { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD3: // int 64 { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xDC: // array 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDD: // array 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDE: // map 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } case 0xDF: // map 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } @@ -1473,19 +1471,19 @@ class binary_reader case 0xD9: // str 8 { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDA: // str 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDB: // str 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } @@ -1520,29 +1518,29 @@ class binary_reader { case 0xC4: // bin 8 { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { - std::uint8_t len; - std::int8_t subtype; + std::uint8_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -1551,8 +1549,8 @@ class binary_reader case 0xC8: // ext 16 { - std::uint16_t len; - std::int8_t subtype; + std::uint16_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -1561,8 +1559,8 @@ class binary_reader case 0xC9: // ext 32 { - std::uint32_t len; - std::int8_t subtype; + std::uint32_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -1571,7 +1569,7 @@ class binary_reader case 0xD4: // fixext 1 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 1, result) and assign_and_return_true(subtype); @@ -1579,7 +1577,7 @@ class binary_reader case 0xD5: // fixext 2 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 2, result) and assign_and_return_true(subtype); @@ -1587,7 +1585,7 @@ class binary_reader case 0xD6: // fixext 4 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 4, result) and assign_and_return_true(subtype); @@ -1595,7 +1593,7 @@ class binary_reader case 0xD7: // fixext 8 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 8, result) and assign_and_return_true(subtype); @@ -1603,7 +1601,7 @@ class binary_reader case 0xD8: // fixext 16 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 16, result) and assign_and_return_true(subtype); @@ -1712,31 +1710,31 @@ class binary_reader { case 'U': { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'i': { - std::int8_t len; + std::int8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'I': { - std::int16_t len; + std::int16_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'l': { - std::int32_t len; + std::int32_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'L': { - std::int64_t len; + std::int64_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } @@ -1756,7 +1754,7 @@ class binary_reader { case 'U': { - std::uint8_t number; + std::uint8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1767,7 +1765,7 @@ class binary_reader case 'i': { - std::int8_t number; + std::int8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1778,7 +1776,7 @@ class binary_reader case 'I': { - std::int16_t number; + std::int16_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1789,7 +1787,7 @@ class binary_reader case 'l': { - std::int32_t number; + std::int32_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1800,7 +1798,7 @@ class binary_reader case 'L': { - std::int64_t number; + std::int64_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1885,43 +1883,43 @@ class binary_reader case 'U': { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); } case 'i': { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'I': { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'l': { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'L': { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'd': { - float number; + float number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } case 'D': { - double number; + double number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index ed9db0f6f..47ea4a5b1 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -76,7 +76,7 @@ class input_stream_adapter { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - if (is) + if (is != nullptr) { is->clear(is->rdstate() & std::ios::eofbit); } @@ -411,7 +411,7 @@ template < typename CharT, contiguous_bytes_input_adapter input_adapter(CharT b) { auto length = std::strlen(reinterpret_cast(b)); - auto ptr = reinterpret_cast(b); + const auto* ptr = reinterpret_cast(b); return input_adapter(ptr, ptr + length); } diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 66b08bff7..c7c1db81e 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -269,16 +269,16 @@ class json_sax_dom_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP @@ -523,16 +523,16 @@ class json_sax_dom_callback_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 0ff0c7362..6d188e470 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -131,7 +131,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } diff --git a/include/nlohmann/detail/iterators/internal_iterator.hpp b/include/nlohmann/detail/iterators/internal_iterator.hpp index 742df483a..2c81f723f 100644 --- a/include/nlohmann/detail/iterators/internal_iterator.hpp +++ b/include/nlohmann/detail/iterators/internal_iterator.hpp @@ -18,8 +18,6 @@ template struct internal_iterator typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; - /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; diff --git a/include/nlohmann/detail/json_ref.hpp b/include/nlohmann/detail/json_ref.hpp index c8dec7330..c9bf6cb22 100644 --- a/include/nlohmann/detail/json_ref.hpp +++ b/include/nlohmann/detail/json_ref.hpp @@ -16,23 +16,30 @@ class json_ref using value_type = BasicJsonType; json_ref(value_type&& value) - : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast(&value)), is_rvalue(false) + : value_ref(const_cast(&value)) + , is_rvalue(false) {} json_ref(std::initializer_list init) - : owned_value(init), value_ref(&owned_value), is_rvalue(true) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) {} template < class... Args, enable_if_t::value, int> = 0 > json_ref(Args && ... args) - : owned_value(std::forward(args)...), value_ref(&owned_value), - is_rvalue(true) {} + : owned_value(std::forward(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} // class should be movable only json_ref(json_ref&&) = default; @@ -63,7 +70,7 @@ class json_ref private: mutable value_type owned_value = nullptr; value_type* value_ref = nullptr; - const bool is_rvalue; + const bool is_rvalue = true; }; } // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 7289da3dc..3bac02270 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -573,7 +573,7 @@ class binary_writer const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; + std::uint8_t output_type{}; bool fixed = true; if (use_ext) { @@ -615,30 +615,18 @@ class binary_writer } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC8; // ext 16 - } - else - { - output_type = 0xC5; // bin 16 - } + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC9; // ext 32 - } - else - { - output_type = 0xC6; // bin 32 - } + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index e8f31fd8a..d86113b37 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -9,7 +9,7 @@ #include // uint8_t #include // snprintf #include // numeric_limits -#include // string +#include // string, char_traits #include // is_same #include // move @@ -59,8 +59,8 @@ class serializer error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) - , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)) - , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) , indent_char(ichar) , indent_string(512, indent_char) , error_handler(error_handler_) @@ -701,7 +701,7 @@ class serializer } // use a pointer to fill the buffer - auto buffer_ptr = number_buffer.begin(); + auto* buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e2aea1037..d0e01c861 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4502,7 +4502,7 @@ class input_stream_adapter { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - if (is) + if (is != nullptr) { is->clear(is->rdstate() & std::ios::eofbit); } @@ -4837,7 +4837,7 @@ template < typename CharT, contiguous_bytes_input_adapter input_adapter(CharT b) { auto length = std::strlen(reinterpret_cast(b)); - auto ptr = reinterpret_cast(b); + const auto* ptr = reinterpret_cast(b); return input_adapter(ptr, ptr + length); } @@ -5154,16 +5154,16 @@ class json_sax_dom_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP @@ -5408,16 +5408,16 @@ class json_sax_dom_callback_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP @@ -5886,7 +5886,7 @@ class binary_reader */ bool parse_bson_internal() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) @@ -5925,8 +5925,6 @@ class binary_reader } *out++ = static_cast(current); } - - return true; } /*! @@ -5971,7 +5969,7 @@ class binary_reader } // All BSON binary values have a subtype - std::uint8_t subtype; + std::uint8_t subtype{}; get_number(input_format_t::bson, subtype); result.set_subtype(subtype); @@ -5995,13 +5993,13 @@ class binary_reader { case 0x01: // double { - double number; + double number{}; return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); } case 0x02: // string { - std::int32_t len; + std::int32_t len{}; string_t value; return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); } @@ -6018,7 +6016,7 @@ class binary_reader case 0x05: // binary { - std::int32_t len; + std::int32_t len{}; binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -6035,13 +6033,13 @@ class binary_reader case 0x10: // int32 { - std::int32_t value; + std::int32_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } case 0x12: // int64 { - std::int64_t value; + std::int64_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } @@ -6106,7 +6104,7 @@ class binary_reader */ bool parse_bson_array() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) @@ -6170,25 +6168,25 @@ class binary_reader case 0x18: // Unsigned integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x19: // Unsigned integer (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1A: // Unsigned integer (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1B: // Unsigned integer (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } @@ -6221,25 +6219,25 @@ class binary_reader case 0x38: // Negative integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - static_cast(number)); } @@ -6343,25 +6341,25 @@ class binary_reader case 0x98: // array (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x99: // array (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9A: // array (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9B: // array (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } @@ -6397,25 +6395,25 @@ class binary_reader case 0xB8: // map (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xB9: // map (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBA: // map (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBB: // map (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } @@ -6481,13 +6479,13 @@ class binary_reader case 0xFA: // Single-Precision Float (four-byte IEEE 754) { - float number; + float number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { - double number; + double number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } @@ -6550,25 +6548,25 @@ class binary_reader case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } @@ -6645,28 +6643,28 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } @@ -7031,85 +7029,85 @@ class binary_reader case 0xCA: // float 32 { - float number; + float number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCB: // float 64 { - double number; + double number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCC: // uint 8 { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCD: // uint 16 { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCE: // uint 32 { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCF: // uint 64 { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xD0: // int 8 { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD1: // int 16 { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD2: // int 32 { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD3: // int 64 { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xDC: // array 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDD: // array 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDE: // map 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } case 0xDF: // map 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } @@ -7214,19 +7212,19 @@ class binary_reader case 0xD9: // str 8 { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDA: // str 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDB: // str 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } @@ -7261,29 +7259,29 @@ class binary_reader { case 0xC4: // bin 8 { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { - std::uint8_t len; - std::int8_t subtype; + std::uint8_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -7292,8 +7290,8 @@ class binary_reader case 0xC8: // ext 16 { - std::uint16_t len; - std::int8_t subtype; + std::uint16_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -7302,8 +7300,8 @@ class binary_reader case 0xC9: // ext 32 { - std::uint32_t len; - std::int8_t subtype; + std::uint32_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -7312,7 +7310,7 @@ class binary_reader case 0xD4: // fixext 1 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 1, result) and assign_and_return_true(subtype); @@ -7320,7 +7318,7 @@ class binary_reader case 0xD5: // fixext 2 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 2, result) and assign_and_return_true(subtype); @@ -7328,7 +7326,7 @@ class binary_reader case 0xD6: // fixext 4 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 4, result) and assign_and_return_true(subtype); @@ -7336,7 +7334,7 @@ class binary_reader case 0xD7: // fixext 8 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 8, result) and assign_and_return_true(subtype); @@ -7344,7 +7342,7 @@ class binary_reader case 0xD8: // fixext 16 { - std::int8_t subtype; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, 16, result) and assign_and_return_true(subtype); @@ -7453,31 +7451,31 @@ class binary_reader { case 'U': { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'i': { - std::int8_t len; + std::int8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'I': { - std::int16_t len; + std::int16_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'l': { - std::int32_t len; + std::int32_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'L': { - std::int64_t len; + std::int64_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } @@ -7497,7 +7495,7 @@ class binary_reader { case 'U': { - std::uint8_t number; + std::uint8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7508,7 +7506,7 @@ class binary_reader case 'i': { - std::int8_t number; + std::int8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7519,7 +7517,7 @@ class binary_reader case 'I': { - std::int16_t number; + std::int16_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7530,7 +7528,7 @@ class binary_reader case 'l': { - std::int32_t number; + std::int32_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7541,7 +7539,7 @@ class binary_reader case 'L': { - std::int64_t number; + std::int64_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7626,43 +7624,43 @@ class binary_reader case 'U': { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); } case 'i': { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'I': { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'l': { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'L': { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'd': { - float number; + float number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } case 'D': { - double number; + double number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } @@ -8202,7 +8200,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -10249,8 +10247,6 @@ template struct internal_iterator typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; - /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; @@ -12026,23 +12022,30 @@ class json_ref using value_type = BasicJsonType; json_ref(value_type&& value) - : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast(&value)), is_rvalue(false) + : value_ref(const_cast(&value)) + , is_rvalue(false) {} json_ref(std::initializer_list init) - : owned_value(init), value_ref(&owned_value), is_rvalue(true) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) {} template < class... Args, enable_if_t::value, int> = 0 > json_ref(Args && ... args) - : owned_value(std::forward(args)...), value_ref(&owned_value), - is_rvalue(true) {} + : owned_value(std::forward(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} // class should be movable only json_ref(json_ref&&) = default; @@ -12073,7 +12076,7 @@ class json_ref private: mutable value_type owned_value = nullptr; value_type* value_ref = nullptr; - const bool is_rvalue; + const bool is_rvalue = true; }; } // namespace detail } // namespace nlohmann @@ -12787,7 +12790,7 @@ class binary_writer const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; + std::uint8_t output_type{}; bool fixed = true; if (use_ext) { @@ -12829,30 +12832,18 @@ class binary_writer } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC8; // ext 16 - } - else - { - output_type = 0xC5; // bin 16 - } + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC9; // ext 32 - } - else - { - output_type = 0xC6; // bin 32 - } + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); @@ -13806,7 +13797,7 @@ class binary_writer #include // uint8_t #include // snprintf #include // numeric_limits -#include // string +#include // string, char_traits #include // is_same #include // move @@ -14973,8 +14964,8 @@ class serializer error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) - , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)) - , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) , indent_char(ichar) , indent_string(512, indent_char) , error_handler(error_handler_) @@ -15615,7 +15606,7 @@ class serializer } // use a pointer to fill the buffer - auto buffer_ptr = number_buffer.begin(); + auto* buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; From a9809f3381d4eb03d54fa2dddb23753903678f93 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 22 Jun 2020 23:02:28 +0200 Subject: [PATCH 28/49] :checkered_flag: revert change that breaks with MSVC --- include/nlohmann/detail/output/serializer.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index d86113b37..c089b94fc 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -701,7 +701,7 @@ class serializer } // use a pointer to fill the buffer - auto* buffer_ptr = number_buffer.begin(); + auto buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index d0e01c861..affa48c99 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -15606,7 +15606,7 @@ class serializer } // use a pointer to fill the buffer - auto* buffer_ptr = number_buffer.begin(); + auto buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; From 0ecf297457272a887a454508bb66610e9156a920 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Tue, 23 Jun 2020 09:14:55 +0800 Subject: [PATCH 29/49] drop std::enable_if part --- include/nlohmann/detail/iterators/iteration_proxy.hpp | 3 +-- single_include/nlohmann/json.hpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index d19be4ddd..7d0f1e591 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -12,8 +12,7 @@ namespace nlohmann { namespace detail { -template::value, int>::type = 0> +template void int_to_string( string_type& target, std::size_t value ) { // For ADL diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 58b12a7df..a6f9af99b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3650,8 +3650,7 @@ namespace nlohmann { namespace detail { -template::value, int>::type = 0> +template void int_to_string( string_type& target, std::size_t value ) { // For ADL From ec43371e07a903e6ffe74c87309a0811905a3a3c Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Fri, 26 Jun 2020 11:47:36 -0700 Subject: [PATCH 30/49] Enable CMake policy CMP0077 Projects that import json via [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) or `add_subdirectory` pointed at a git submodule may want to set `JSON_BuildTests` to "NO". However, this doesn't work without creating an identical `option()` in the importing project. Enabling CMP0077 in supported versions of CMake changes the behavior of `option()` to allow importing projects to set default values for the variables without touching the cache. See the documentation for CMP0077 here: https://cmake.org/cmake/help/latest/policy/CMP0077.html --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f163041f..f7ba4fa38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,12 @@ include(ExternalProject) ## ## OPTIONS ## + +if (POLICY CMP0077) + # Allow CMake 3.13+ to override options when using FetchContent / add_subdirectory. + cmake_policy(SET CMP0077 NEW) +endif () + option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON) option(JSON_Install "Install CMake targets during install step." ON) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) From aefa0b3e865a19224d030a07984b7a8a52169d93 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 27 Jun 2020 12:54:42 +0200 Subject: [PATCH 31/49] :wrench: use Github discussions for questions --- .github/ISSUE_TEMPLATE/config.yml | 5 ++++ .github/ISSUE_TEMPLATE/question.md | 40 ------------------------------ 2 files changed, 5 insertions(+), 40 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..0e9663384 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/nlohmann/json/discussions + about: Ask questions and discuss with other community members diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 3f522cde7..000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: Question -about: Ask a question regarding the library. -title: '' -labels: 'kind: question' -assignees: '' - ---- - - - -#### What do you want to achieve? - - - -#### What have you tried? - - - - - -#### Can you provide a small code example? - - - -#### Which compiler and operating system are you using? - - - - -- Compiler: ___ -- Operating system: ___ - -#### Which version of the library did you use? - - - -- [ ] latest release version 3.7.3 -- [ ] other release - please state the version: ___ -- [ ] the `develop` branch From 5ba0f65c34832aa18fb2b582d58a0c1f92c93bfb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 27 Jun 2020 12:55:41 +0200 Subject: [PATCH 32/49] :wrench: remove feature request template --- .github/ISSUE_TEMPLATE/Feature_request.md | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index f7effbaf6..000000000 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: 'kind: enhancement/improvement' -assignees: '' - ---- - -#### Which feature do you want to see in the library? - - - -#### How would the feature be usable for other users? - - From fa9f4040dffe11792d473552f7786e8bc60f290c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 27 Jun 2020 13:07:02 +0200 Subject: [PATCH 33/49] :fire: remove unused typedefs #2221 --- include/nlohmann/detail/json_pointer.hpp | 6 ------ single_include/nlohmann/json.hpp | 6 ------ 2 files changed, 12 deletions(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 562802920..ea14861bc 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -398,7 +398,6 @@ class json_pointer */ BasicJsonType& get_and_create(BasicJsonType& j) const { - using size_type = typename BasicJsonType::size_type; auto result = &j; // in case no reference tokens exist, return a reference to the JSON value @@ -471,7 +470,6 @@ class json_pointer */ BasicJsonType& get_unchecked(BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { // convert null values to arrays or objects before continuing @@ -531,7 +529,6 @@ class json_pointer */ BasicJsonType& get_checked(BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -581,7 +578,6 @@ class json_pointer */ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -624,7 +620,6 @@ class json_pointer */ const BasicJsonType& get_checked(const BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -665,7 +660,6 @@ class json_pointer */ bool contains(const BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 770418f46..3a4fc0224 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11529,7 +11529,6 @@ class json_pointer */ BasicJsonType& get_and_create(BasicJsonType& j) const { - using size_type = typename BasicJsonType::size_type; auto result = &j; // in case no reference tokens exist, return a reference to the JSON value @@ -11602,7 +11601,6 @@ class json_pointer */ BasicJsonType& get_unchecked(BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { // convert null values to arrays or objects before continuing @@ -11662,7 +11660,6 @@ class json_pointer */ BasicJsonType& get_checked(BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -11712,7 +11709,6 @@ class json_pointer */ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -11755,7 +11751,6 @@ class json_pointer */ const BasicJsonType& get_checked(const BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) @@ -11796,7 +11791,6 @@ class json_pointer */ bool contains(const BasicJsonType* ptr) const { - using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->type()) From 470f7c0c68a967e853781cde039486ab93866431 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 29 Jun 2020 13:43:06 +0200 Subject: [PATCH 34/49] :memo: add documentation --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index d7492da34..1052cc0c5 100644 --- a/README.md +++ b/README.md @@ -869,6 +869,42 @@ Some important things: * In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. +#### Simplify your life with macros + +If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. + +There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: + +- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for. +- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members. + +In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. + +##### Examples + +The `to_json`/`from_json` functions for the `person` struct above can be created with: + +```cpp +namespace ns { + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age); +} +``` + +Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed: + +```cpp +namespace ns { + class address { + private: + std::string street; + int housenumber; + int postcode; + + public: + NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode); + }; +} +``` #### How do I convert third-party types? From ad6eadeb70b6ba20cec985dea8da829fda2fbdd9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 30 Jun 2020 13:59:43 +0200 Subject: [PATCH 35/49] :memo: refine documentation of error_handler parameter --- include/nlohmann/json.hpp | 3 ++- single_include/nlohmann/json.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index ace649b66..998aa7200 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2238,7 +2238,8 @@ class basic_json @param[in] error_handler how to react on decoding errors; there are three possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), - and `ignore` (ignore invalid UTF-8 sequences during serialization). + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). @return string containing the serialization of the JSON value diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7ab24c844..426ee8157 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18168,7 +18168,8 @@ class basic_json @param[in] error_handler how to react on decoding errors; there are three possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), - and `ignore` (ignore invalid UTF-8 sequences during serialization). + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). @return string containing the serialization of the JSON value From c7e079cc98a6c5534e8d4a209dd0984e569d0625 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 30 Jun 2020 14:26:52 +0200 Subject: [PATCH 36/49] :ambulance: add specialization of get_to #2175 --- include/nlohmann/json.hpp | 12 ++++++++++++ single_include/nlohmann/json.hpp | 12 ++++++++++++ test/src/unit-udt_macro.cpp | 28 +++++++++++++++++----------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index ace649b66..00b7ce555 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3019,6 +3019,18 @@ class basic_json return v; } + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + template < typename T, std::size_t N, typename Array = T (&)[N], diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7ab24c844..9cba3069c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18949,6 +18949,18 @@ class basic_json return v; } + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + template < typename T, std::size_t N, typename Array = T (&)[N], diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index ab2d32451..a45b2c509 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -41,20 +41,22 @@ class person_with_private_data private: std::string name; int age = 0; + json metadata; public: bool operator==(const person_with_private_data& rhs) const { - return std::tie(name, age) == std::tie(rhs.name, rhs.age); + return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata); } person_with_private_data() = default; - person_with_private_data(std::string name, int age) + person_with_private_data(std::string name, int age, json metadata) : name(std::move(name)) , age(age) + , metadata(std::move(metadata)) {} - NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name); + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata); }; class person_without_private_data_1 @@ -62,19 +64,21 @@ class person_without_private_data_1 public: std::string name; int age = 0; + json metadata; bool operator==(const person_without_private_data_1& rhs) const { - return std::tie(name, age) == std::tie(rhs.name, rhs.age); + return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata); } person_without_private_data_1() = default; - person_without_private_data_1(std::string name, int age) + person_without_private_data_1(std::string name, int age, json metadata) : name(std::move(name)) , age(age) + , metadata(std::move(metadata)) {} - NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name); + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata); }; class person_without_private_data_2 @@ -82,20 +86,22 @@ class person_without_private_data_2 public: std::string name; int age = 0; + json metadata; bool operator==(const person_without_private_data_2& rhs) const { - return std::tie(name, age) == std::tie(rhs.name, rhs.age); + return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata); } person_without_private_data_2() = default; - person_without_private_data_2(std::string name, int age) + person_without_private_data_2(std::string name, int age, json metadata) : name(std::move(name)) , age(age) + , metadata(std::move(metadata)) {} }; -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name); +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata); } // namespace persons TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T, @@ -106,8 +112,8 @@ TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRU SECTION("person") { // serialization - T p1("Erik", 1); - CHECK(json(p1).dump() == "{\"age\":1,\"name\":\"Erik\"}"); + T p1("Erik", 1, {{"haircuts", 2}}); + CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); // deserialization T p2 = json(p1); From f59f4a2b61161f8aab0d7b499fbb9b7a94b66188 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 30 Jun 2020 19:55:40 +0200 Subject: [PATCH 37/49] :green_heart: fix build --- test/src/unit-udt_macro.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index a45b2c509..4535ae5b5 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -32,8 +32,6 @@ SOFTWARE. #include using nlohmann::json; -#include - namespace persons { class person_with_private_data @@ -46,7 +44,7 @@ class person_with_private_data public: bool operator==(const person_with_private_data& rhs) const { - return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata); + return name == rhs.name && age == rhs.age && metadata == rhs.metadata; } person_with_private_data() = default; @@ -68,7 +66,7 @@ class person_without_private_data_1 bool operator==(const person_without_private_data_1& rhs) const { - return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata); + return name == rhs.name && age == rhs.age && metadata == rhs.metadata; } person_without_private_data_1() = default; @@ -90,7 +88,7 @@ class person_without_private_data_2 bool operator==(const person_without_private_data_2& rhs) const { - return std::tie(name, age, metadata) == std::tie(rhs.name, rhs.age, rhs.metadata); + return name == rhs.name && age == rhs.age && metadata == rhs.metadata; } person_without_private_data_2() = default; From 98b1c6d302d6b5eb4be0a3909e1dc09f920d8154 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 6 Jul 2020 12:22:31 +0200 Subject: [PATCH 38/49] :triangular_flag_on_post: use JSON_ASSERT(x) instead of assert(x) --- README.md | 2 +- .../nlohmann/detail/conversions/to_chars.hpp | 81 +++--- .../nlohmann/detail/input/binary_reader.hpp | 9 +- .../nlohmann/detail/input/input_adapters.hpp | 9 +- include/nlohmann/detail/input/json_sax.hpp | 29 +- include/nlohmann/detail/input/lexer.hpp | 24 +- include/nlohmann/detail/input/parser.hpp | 5 +- .../nlohmann/detail/iterators/iter_impl.hpp | 34 +-- .../detail/iterators/iteration_proxy.hpp | 2 +- include/nlohmann/detail/json_pointer.hpp | 5 +- include/nlohmann/detail/macro_scope.hpp | 6 + include/nlohmann/detail/macro_unscope.hpp | 1 + .../nlohmann/detail/output/binary_writer.hpp | 12 +- include/nlohmann/detail/output/serializer.hpp | 33 +-- include/nlohmann/json.hpp | 25 +- single_include/nlohmann/json.hpp | 275 +++++++++--------- 16 files changed, 275 insertions(+), 277 deletions(-) diff --git a/README.md b/README.md index 6d21e2d85..7a7a46ad9 100644 --- a/README.md +++ b/README.md @@ -1572,7 +1572,7 @@ Here is a related issue [#1924](https://github.com/nlohmann/json/issues/1924). ### Further notes -- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a233b02b0839ef798942dd46157cc0fe6.html#a233b02b0839ef798942dd46157cc0fe6) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a73ae333487310e3302135189ce8ff5d8.html#a73ae333487310e3302135189ce8ff5d8). +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a233b02b0839ef798942dd46157cc0fe6.html#a233b02b0839ef798942dd46157cc0fe6) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a73ae333487310e3302135189ce8ff5d8.html#a73ae333487310e3302135189ce8ff5d8). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. - As the exact type of a number is not defined in the [JSON specification](https://tools.ietf.org/html/rfc8259.html), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag. - **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER´` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp index 5851fd0ae..0907e7656 100644 --- a/include/nlohmann/detail/conversions/to_chars.hpp +++ b/include/nlohmann/detail/conversions/to_chars.hpp @@ -1,7 +1,6 @@ #pragma once #include // array -#include // assert #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove @@ -63,8 +62,8 @@ struct diyfp // f * 2^e */ static diyfp sub(const diyfp& x, const diyfp& y) noexcept { - assert(x.e == y.e); - assert(x.f >= y.f); + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); return {x.f - y.f, x.e}; } @@ -140,7 +139,7 @@ struct diyfp // f * 2^e */ static diyfp normalize(diyfp x) noexcept { - assert(x.f != 0); + JSON_ASSERT(x.f != 0); while ((x.f >> 63u) == 0) { @@ -159,8 +158,8 @@ struct diyfp // f * 2^e { const int delta = x.e - target_exponent; - assert(delta >= 0); - assert(((x.f << delta) >> delta) == x.f); + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); return {x.f << delta, target_exponent}; } @@ -182,8 +181,8 @@ boundaries. template boundaries compute_boundaries(FloatType value) { - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // Convert the IEEE representation into a diyfp. // @@ -463,18 +462,18 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // k = ceil((kAlpha - e - 1) * 0.30102999566398114) // for |e| <= 1500, but doesn't require floating-point operations. // NB: log_10(2) ~= 78913 / 2^18 - assert(e >= -1500); - assert(e <= 1500); + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; - assert(index >= 0); - assert(static_cast(index) < kCachedPowers.size()); + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); const cached_power cached = kCachedPowers[static_cast(index)]; - assert(kAlpha <= cached.e + e + 64); - assert(kGamma >= cached.e + e + 64); + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); return cached; } @@ -542,10 +541,10 @@ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, std::uint64_t rest, std::uint64_t ten_k) { - assert(len >= 1); - assert(dist <= delta); - assert(rest <= delta); - assert(ten_k > 0); + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); // <--------------------------- delta ----> // <---- dist ---------> @@ -570,7 +569,7 @@ inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t d and delta - rest >= ten_k and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) { - assert(buf[len - 1] != '0'); + JSON_ASSERT(buf[len - 1] != '0'); buf[len - 1]--; rest += ten_k; } @@ -598,8 +597,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // Grisu2 generates the digits of M+ from left to right and stops as soon as // V is in [M-,M+]. - assert(M_plus.e >= kAlpha); - assert(M_plus.e <= kGamma); + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) @@ -620,7 +619,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - assert(p1 > 0); + JSON_ASSERT(p1 > 0); std::uint32_t pow10; const int k = find_largest_pow10(p1, pow10); @@ -656,7 +655,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) // - assert(d <= 9); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) @@ -743,7 +742,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - assert(p2 > delta); + JSON_ASSERT(p2 > delta); int m = 0; for (;;) @@ -754,7 +753,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e // - assert(p2 <= (std::numeric_limits::max)() / 10); + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); p2 *= 10; const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e @@ -763,7 +762,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e // - assert(d <= 9); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e @@ -824,8 +823,8 @@ JSON_HEDLEY_NON_NULL(1) inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) { - assert(m_plus.e == m_minus.e); - assert(m_plus.e == v.e); + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); // --------(-----------------------+-----------------------)-------- (A) // m- v m+ @@ -886,8 +885,8 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, "internal error: not enough precision"); - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // If the neighbors (and boundaries) of 'value' are always computed for double-precision // numbers, all float's can be recovered using strtod (and strtof). However, the resulting @@ -923,8 +922,8 @@ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* append_exponent(char* buf, int e) { - assert(e > -1000); - assert(e < 1000); + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); if (e < 0) { @@ -976,8 +975,8 @@ JSON_HEDLEY_RETURNS_NON_NULL inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp) { - assert(min_exp < 0); - assert(max_exp > 0); + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); const int k = len; const int n = len + decimal_exponent; @@ -1003,7 +1002,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // dig.its // len <= max_digits10 + 1 - assert(k > n); + JSON_ASSERT(k > n); std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); buf[n] = '.'; @@ -1061,7 +1060,7 @@ JSON_HEDLEY_RETURNS_NON_NULL char* to_chars(char* first, const char* last, FloatType value) { static_cast(last); // maybe unused - fix warning - assert(std::isfinite(value)); + JSON_ASSERT(std::isfinite(value)); // Use signbit(value) instead of (value < 0) since signbit works for -0. if (std::signbit(value)) @@ -1079,7 +1078,7 @@ char* to_chars(char* first, const char* last, FloatType value) return first; } - assert(last - first >= std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted @@ -1089,16 +1088,16 @@ char* to_chars(char* first, const char* last, FloatType value) int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); - assert(len <= std::numeric_limits::max_digits10); + JSON_ASSERT(len <= std::numeric_limits::max_digits10); // Format the buffer like printf("%.*g", prec, value) constexpr int kMinExp = -4; // Use digits10 here to increase compatibility with version 2. constexpr int kMaxExp = std::numeric_limits::digits10; - assert(last - first >= kMaxExp + 2); - assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); - assert(last - first >= std::numeric_limits::max_digits10 + 6); + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index a27a15387..e075d5272 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -2,7 +2,6 @@ #include // generate_n #include // array -#include // assert #include // ldexp #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t @@ -109,7 +108,7 @@ class binary_reader break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } // strict mode: next byte must be EOF @@ -717,8 +716,8 @@ class binary_reader { const int exp = (half >> 10u) & 0x1Fu; const unsigned int mant = half & 0x3FFu; - assert(0 <= exp and exp <= 32); - assert(mant <= 1024); + JSON_ASSERT(0 <= exp and exp <= 32); + JSON_ASSERT(mant <= 1024); switch (exp) { case 0: @@ -2295,7 +2294,7 @@ class binary_reader break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } return error_msg + " " + context + ": " + detail; diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 47ea4a5b1..2efccb1ad 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -1,7 +1,6 @@ #pragma once #include // array -#include // assert #include // size_t #include //FILE * #include // strlen @@ -297,13 +296,13 @@ class wide_string_input_adapter { fill_buffer(); - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index == 0); + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); } // use buffer - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index < utf8_bytes_filled); + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); return utf8_bytes[utf8_bytes_index++]; } diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index c7c1db81e..18d82822c 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -1,6 +1,5 @@ #pragma once -#include // assert #include #include // string #include // move @@ -280,7 +279,7 @@ class json_sax_dom_parser case 5: JSON_THROW(*dynamic_cast(&ex)); default: - assert(false); + JSON_ASSERT(false); // LCOV_EXCL_STOP } } @@ -309,7 +308,7 @@ class json_sax_dom_parser return &root; } - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object()); if (ref_stack.back()->is_array()) { @@ -317,8 +316,8 @@ class json_sax_dom_parser return &(ref_stack.back()->m_value.array->back()); } - assert(ref_stack.back()->is_object()); - assert(object_element); + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); return object_element; } @@ -447,8 +446,8 @@ class json_sax_dom_callback_parser *ref_stack.back() = discarded; } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); + JSON_ASSERT(not ref_stack.empty()); + JSON_ASSERT(not keep_stack.empty()); ref_stack.pop_back(); keep_stack.pop_back(); @@ -499,8 +498,8 @@ class json_sax_dom_callback_parser } } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); + JSON_ASSERT(not ref_stack.empty()); + JSON_ASSERT(not keep_stack.empty()); ref_stack.pop_back(); keep_stack.pop_back(); @@ -534,7 +533,7 @@ class json_sax_dom_callback_parser case 5: JSON_THROW(*dynamic_cast(&ex)); default: - assert(false); + JSON_ASSERT(false); // LCOV_EXCL_STOP } } @@ -565,7 +564,7 @@ class json_sax_dom_callback_parser template std::pair handle_value(Value&& v, const bool skip_callback = false) { - assert(not keep_stack.empty()); + JSON_ASSERT(not keep_stack.empty()); // do not handle this value if we know it would be added to a discarded // container @@ -600,7 +599,7 @@ class json_sax_dom_callback_parser } // we now only expect arrays and objects - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object()); // array if (ref_stack.back()->is_array()) @@ -610,9 +609,9 @@ class json_sax_dom_callback_parser } // object - assert(ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_object()); // check if we should store an element for the current key - assert(not key_keep_stack.empty()); + JSON_ASSERT(not key_keep_stack.empty()); const bool store_element = key_keep_stack.back(); key_keep_stack.pop_back(); @@ -621,7 +620,7 @@ class json_sax_dom_callback_parser return {false, nullptr}; } - assert(object_element); + JSON_ASSERT(object_element); *object_element = std::move(value); return {true, object_element}; } diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 8042f3c4e..0a0c6c74e 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -135,7 +135,7 @@ class lexer : public lexer_base static char get_decimal_point() noexcept { const auto* loc = localeconv(); - assert(loc != nullptr); + JSON_ASSERT(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -161,7 +161,7 @@ class lexer : public lexer_base int get_codepoint() { // this function only makes sense after reading `\u` - assert(current == 'u'); + JSON_ASSERT(current == 'u'); int codepoint = 0; const auto factors = { 12u, 8u, 4u, 0u }; @@ -187,7 +187,7 @@ class lexer : public lexer_base } } - assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + JSON_ASSERT(0x0000 <= codepoint and codepoint <= 0xFFFF); return codepoint; } @@ -208,7 +208,7 @@ class lexer : public lexer_base */ bool next_byte_in_range(std::initializer_list ranges) { - assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + JSON_ASSERT(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); add(current); for (auto range = ranges.begin(); range != ranges.end(); ++range) @@ -249,7 +249,7 @@ class lexer : public lexer_base reset(); // we entered the function by reading an open quote - assert(current == '\"'); + JSON_ASSERT(current == '\"'); while (true) { @@ -369,7 +369,7 @@ class lexer : public lexer_base } // result of the above calculation yields a proper codepoint - assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + JSON_ASSERT(0x00 <= codepoint and codepoint <= 0x10FFFF); // translate codepoint into bytes if (codepoint < 0x80) @@ -998,7 +998,7 @@ class lexer : public lexer_base // all other characters are rejected outside scan_number() default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } scan_number_minus: @@ -1245,7 +1245,7 @@ scan_number_done: const auto x = std::strtoull(token_buffer.data(), &endptr, 10); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -1261,7 +1261,7 @@ scan_number_done: const auto x = std::strtoll(token_buffer.data(), &endptr, 10); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -1278,7 +1278,7 @@ scan_number_done: strtof(value_float, token_buffer.data(), &endptr); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); return token_type::value_float; } @@ -1292,7 +1292,7 @@ scan_number_done: token_type scan_literal(const char_type* literal_text, const std::size_t length, token_type return_type) { - assert(std::char_traits::to_char_type(current) == literal_text[0]); + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) @@ -1384,7 +1384,7 @@ scan_number_done: if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { - assert(not token_string.empty()); + JSON_ASSERT(not token_string.empty()); token_string.pop_back(); } } diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index c79b492aa..b9f7cd842 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -1,6 +1,5 @@ #pragma once -#include // assert #include // isfinite #include // uint8_t #include // function @@ -383,7 +382,7 @@ class parser // new value, we need to evaluate the new state first. // By setting skip_to_state_evaluation to false, we // are effectively jumping to the beginning of this if. - assert(not states.empty()); + JSON_ASSERT(not states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; @@ -439,7 +438,7 @@ class parser // new value, we need to evaluate the new state first. // By setting skip_to_state_evaluation to false, we // are effectively jumping to the beginning of this if. - assert(not states.empty()); + JSON_ASSERT(not states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 0b79202a5..8d7796e11 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -85,7 +85,7 @@ class iter_impl */ explicit iter_impl(pointer object) noexcept : m_object(object) { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -171,7 +171,7 @@ class iter_impl */ void set_begin() noexcept { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -208,7 +208,7 @@ class iter_impl */ void set_end() noexcept { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -239,19 +239,19 @@ class iter_impl */ reference operator*() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { - assert(m_it.object_iterator != m_object->m_value.object->end()); + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { - assert(m_it.array_iterator != m_object->m_value.array->end()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -276,19 +276,19 @@ class iter_impl */ pointer operator->() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { - assert(m_it.object_iterator != m_object->m_value.object->end()); + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { - assert(m_it.array_iterator != m_object->m_value.array->end()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -321,7 +321,7 @@ class iter_impl */ iter_impl& operator++() { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -364,7 +364,7 @@ class iter_impl */ iter_impl& operator--() { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -402,7 +402,7 @@ class iter_impl JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -438,7 +438,7 @@ class iter_impl JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -486,7 +486,7 @@ class iter_impl */ iter_impl& operator+=(difference_type i) { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -557,7 +557,7 @@ class iter_impl */ difference_type operator-(const iter_impl& other) const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -578,7 +578,7 @@ class iter_impl */ reference operator[](difference_type n) const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -609,7 +609,7 @@ class iter_impl */ const typename object_t::key_type& key() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); if (JSON_HEDLEY_LIKELY(m_object->is_object())) { diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index 7d0f1e591..b10d0587b 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -74,7 +74,7 @@ template class iteration_proxy_value /// return key of the iterator const string_type& key() const { - assert(anchor.m_object != nullptr); + JSON_ASSERT(anchor.m_object != nullptr); switch (anchor.m_object->type()) { diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index ea14861bc..1ac86591c 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -1,7 +1,6 @@ #pragma once #include // all_of -#include // assert #include // isdigit #include // max #include // accumulate @@ -781,7 +780,7 @@ class json_pointer pos != std::string::npos; pos = reference_token.find_first_of('~', pos + 1)) { - assert(reference_token[pos] == '~'); + JSON_ASSERT(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or @@ -816,7 +815,7 @@ class json_pointer static void replace_substring(std::string& s, const std::string& f, const std::string& t) { - assert(not f.empty()); + JSON_ASSERT(not f.empty()); for (auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 46fa8c101..3537768f0 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -77,6 +77,12 @@ #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif +// allow to override assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index 80b293e7d..044150610 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -9,6 +9,7 @@ #endif // clean up +#undef JSON_ASSERT #undef JSON_INTERNAL_CATCH #undef JSON_CATCH #undef JSON_THROW diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 3bac02270..844791a04 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -38,7 +38,7 @@ class binary_writer */ explicit binary_writer(output_adapter_t adapter) : oa(adapter) { - assert(oa); + JSON_ASSERT(oa); } /*! @@ -754,7 +754,7 @@ class binary_writer bool prefix_required = true; if (use_type and not j.m_value.array->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) @@ -798,7 +798,7 @@ class binary_writer if (use_type and not j.m_value.binary->empty()) { - assert(use_count); + JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); oa->write_character('U'); } @@ -842,7 +842,7 @@ class binary_writer bool prefix_required = true; if (use_type and not j.m_value.object->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) @@ -1134,7 +1134,7 @@ class binary_writer // LCOV_EXCL_START default: - assert(false); + JSON_ASSERT(false); return 0ul; // LCOV_EXCL_STOP } @@ -1181,7 +1181,7 @@ class binary_writer // LCOV_EXCL_START default: - assert(false); + JSON_ASSERT(false); return; // LCOV_EXCL_STOP } diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index c089b94fc..808d93687 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -2,7 +2,6 @@ #include // reverse, remove, fill, find, none_of #include // array -#include // assert #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t @@ -135,8 +134,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); @@ -163,8 +162,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); @@ -205,7 +204,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); @@ -226,7 +225,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(not val.m_value.array->empty()); dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); @@ -360,7 +359,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } } @@ -559,7 +558,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } break; } @@ -622,7 +621,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } } } @@ -723,7 +722,7 @@ class serializer } // spare 1 byte for '\0' - assert(n_chars < number_buffer.size() - 1); + JSON_ASSERT(n_chars < number_buffer.size() - 1); // jump to the end to generate the string from backward // so we later avoid reversing the result @@ -799,9 +798,9 @@ class serializer std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error - assert(len > 0); + JSON_ASSERT(len > 0); // check if buffer was large enough - assert(static_cast(len) < number_buffer.size()); + JSON_ASSERT(static_cast(len) < number_buffer.size()); // erase thousands separator if (thousands_sep != '\0') @@ -809,7 +808,7 @@ class serializer const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); + JSON_ASSERT((end - number_buffer.begin()) <= len); len = (end - number_buffer.begin()); } @@ -889,7 +888,7 @@ class serializer : (0xFFu >> type) & (byte); std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); - assert(index < 400); + JSON_ASSERT(index < 400); state = utf8d[index]; return state; } @@ -901,7 +900,7 @@ class serializer */ number_unsigned_t remove_sign(number_unsigned_t x) { - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE return x; // LCOV_EXCL_LINE } @@ -916,7 +915,7 @@ class serializer */ inline number_unsigned_t remove_sign(number_integer_t x) noexcept { - assert(x < 0 and x < (std::numeric_limits::max)()); + JSON_ASSERT(x < 0 and x < (std::numeric_limits::max)()); return static_cast(-(x + 1)) + 1; } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 1dedb809b..245fa3919 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -35,7 +35,6 @@ SOFTWARE. #define NLOHMANN_JSON_VERSION_PATCH 0 #include // all_of, find, for_each -#include // assert #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list @@ -924,7 +923,7 @@ class basic_json }; std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); - assert(object != nullptr); + JSON_ASSERT(object != nullptr); return object.release(); } @@ -1219,10 +1218,10 @@ class basic_json */ void assert_invariant() const noexcept { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); - assert(m_type != value_t::binary or m_value.binary != nullptr); + JSON_ASSERT(m_type != value_t::object or m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array or m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string or m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary or m_value.binary != nullptr); } public: @@ -1515,7 +1514,7 @@ class basic_json m_type = value_t::discarded; break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } assert_invariant(); } @@ -1915,8 +1914,8 @@ class basic_json std::is_same::value, int>::type = 0> basic_json(InputIT first, InputIT last) { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) @@ -3633,7 +3632,7 @@ class basic_json // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -3725,7 +3724,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -5493,7 +5492,7 @@ class basic_json iterator insert_iterator(const_iterator pos, Args&& ... args) { iterator result(this); - assert(m_value.array != nullptr); + JSON_ASSERT(m_value.array != nullptr); auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); @@ -8258,7 +8257,7 @@ class basic_json // if there exists a parent it cannot be primitive default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 87222900b..a2940ed0c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -35,7 +35,6 @@ SOFTWARE. #define NLOHMANN_JSON_VERSION_PATCH 0 #include // all_of, find, for_each -#include // assert #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list @@ -2105,6 +2104,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif +// allow to override assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM @@ -3757,7 +3762,7 @@ template class iteration_proxy_value /// return key of the iterator const string_type& key() const { - assert(anchor.m_object != nullptr); + JSON_ASSERT(anchor.m_object != nullptr); switch (anchor.m_object->type()) { @@ -4451,7 +4456,6 @@ class byte_container_with_subtype : public BinaryType #include // generate_n #include // array -#include // assert #include // ldexp #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t @@ -4468,7 +4472,6 @@ class byte_container_with_subtype : public BinaryType #include // array -#include // assert #include // size_t #include //FILE * #include // strlen @@ -4766,13 +4769,13 @@ class wide_string_input_adapter { fill_buffer(); - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index == 0); + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); } // use buffer - assert(utf8_bytes_filled > 0); - assert(utf8_bytes_index < utf8_bytes_filled); + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); return utf8_bytes[utf8_bytes_index++]; } @@ -4926,7 +4929,6 @@ class span_input_adapter // #include -#include // assert #include #include // string #include // move @@ -5208,7 +5210,7 @@ class json_sax_dom_parser case 5: JSON_THROW(*dynamic_cast(&ex)); default: - assert(false); + JSON_ASSERT(false); // LCOV_EXCL_STOP } } @@ -5237,7 +5239,7 @@ class json_sax_dom_parser return &root; } - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object()); if (ref_stack.back()->is_array()) { @@ -5245,8 +5247,8 @@ class json_sax_dom_parser return &(ref_stack.back()->m_value.array->back()); } - assert(ref_stack.back()->is_object()); - assert(object_element); + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); *object_element = BasicJsonType(std::forward(v)); return object_element; } @@ -5375,8 +5377,8 @@ class json_sax_dom_callback_parser *ref_stack.back() = discarded; } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); + JSON_ASSERT(not ref_stack.empty()); + JSON_ASSERT(not keep_stack.empty()); ref_stack.pop_back(); keep_stack.pop_back(); @@ -5427,8 +5429,8 @@ class json_sax_dom_callback_parser } } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); + JSON_ASSERT(not ref_stack.empty()); + JSON_ASSERT(not keep_stack.empty()); ref_stack.pop_back(); keep_stack.pop_back(); @@ -5462,7 +5464,7 @@ class json_sax_dom_callback_parser case 5: JSON_THROW(*dynamic_cast(&ex)); default: - assert(false); + JSON_ASSERT(false); // LCOV_EXCL_STOP } } @@ -5493,7 +5495,7 @@ class json_sax_dom_callback_parser template std::pair handle_value(Value&& v, const bool skip_callback = false) { - assert(not keep_stack.empty()); + JSON_ASSERT(not keep_stack.empty()); // do not handle this value if we know it would be added to a discarded // container @@ -5528,7 +5530,7 @@ class json_sax_dom_callback_parser } // we now only expect arrays and objects - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_array() or ref_stack.back()->is_object()); // array if (ref_stack.back()->is_array()) @@ -5538,9 +5540,9 @@ class json_sax_dom_callback_parser } // object - assert(ref_stack.back()->is_object()); + JSON_ASSERT(ref_stack.back()->is_object()); // check if we should store an element for the current key - assert(not key_keep_stack.empty()); + JSON_ASSERT(not key_keep_stack.empty()); const bool store_element = key_keep_stack.back(); key_keep_stack.pop_back(); @@ -5549,7 +5551,7 @@ class json_sax_dom_callback_parser return {false, nullptr}; } - assert(object_element); + JSON_ASSERT(object_element); *object_element = std::move(value); return {true, object_element}; } @@ -5893,7 +5895,7 @@ class binary_reader break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } // strict mode: next byte must be EOF @@ -6501,8 +6503,8 @@ class binary_reader { const int exp = (half >> 10u) & 0x1Fu; const unsigned int mant = half & 0x3FFu; - assert(0 <= exp and exp <= 32); - assert(mant <= 1024); + JSON_ASSERT(0 <= exp and exp <= 32); + JSON_ASSERT(mant <= 1024); switch (exp) { case 0: @@ -8079,7 +8081,7 @@ class binary_reader break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } return error_msg + " " + context + ": " + detail; @@ -8247,7 +8249,7 @@ class lexer : public lexer_base static char get_decimal_point() noexcept { const auto* loc = localeconv(); - assert(loc != nullptr); + JSON_ASSERT(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -8273,7 +8275,7 @@ class lexer : public lexer_base int get_codepoint() { // this function only makes sense after reading `\u` - assert(current == 'u'); + JSON_ASSERT(current == 'u'); int codepoint = 0; const auto factors = { 12u, 8u, 4u, 0u }; @@ -8299,7 +8301,7 @@ class lexer : public lexer_base } } - assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + JSON_ASSERT(0x0000 <= codepoint and codepoint <= 0xFFFF); return codepoint; } @@ -8320,7 +8322,7 @@ class lexer : public lexer_base */ bool next_byte_in_range(std::initializer_list ranges) { - assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + JSON_ASSERT(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); add(current); for (auto range = ranges.begin(); range != ranges.end(); ++range) @@ -8361,7 +8363,7 @@ class lexer : public lexer_base reset(); // we entered the function by reading an open quote - assert(current == '\"'); + JSON_ASSERT(current == '\"'); while (true) { @@ -8481,7 +8483,7 @@ class lexer : public lexer_base } // result of the above calculation yields a proper codepoint - assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + JSON_ASSERT(0x00 <= codepoint and codepoint <= 0x10FFFF); // translate codepoint into bytes if (codepoint < 0x80) @@ -9110,7 +9112,7 @@ class lexer : public lexer_base // all other characters are rejected outside scan_number() default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } scan_number_minus: @@ -9357,7 +9359,7 @@ scan_number_done: const auto x = std::strtoull(token_buffer.data(), &endptr, 10); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -9373,7 +9375,7 @@ scan_number_done: const auto x = std::strtoll(token_buffer.data(), &endptr, 10); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -9390,7 +9392,7 @@ scan_number_done: strtof(value_float, token_buffer.data(), &endptr); // we checked the number format before - assert(endptr == token_buffer.data() + token_buffer.size()); + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); return token_type::value_float; } @@ -9404,7 +9406,7 @@ scan_number_done: token_type scan_literal(const char_type* literal_text, const std::size_t length, token_type return_type) { - assert(std::char_traits::to_char_type(current) == literal_text[0]); + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) @@ -9496,7 +9498,7 @@ scan_number_done: if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { - assert(not token_string.empty()); + JSON_ASSERT(not token_string.empty()); token_string.pop_back(); } } @@ -9737,7 +9739,6 @@ scan_number_done: // #include -#include // assert #include // isfinite #include // uint8_t #include // function @@ -10127,7 +10128,7 @@ class parser // new value, we need to evaluate the new state first. // By setting skip_to_state_evaluation to false, we // are effectively jumping to the beginning of this if. - assert(not states.empty()); + JSON_ASSERT(not states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; @@ -10183,7 +10184,7 @@ class parser // new value, we need to evaluate the new state first. // By setting skip_to_state_evaluation to false, we // are effectively jumping to the beginning of this if. - assert(not states.empty()); + JSON_ASSERT(not states.empty()); states.pop_back(); skip_to_state_evaluation = true; continue; @@ -10489,7 +10490,7 @@ class iter_impl */ explicit iter_impl(pointer object) noexcept : m_object(object) { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10575,7 +10576,7 @@ class iter_impl */ void set_begin() noexcept { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10612,7 +10613,7 @@ class iter_impl */ void set_end() noexcept { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10643,19 +10644,19 @@ class iter_impl */ reference operator*() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { - assert(m_it.object_iterator != m_object->m_value.object->end()); + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { - assert(m_it.array_iterator != m_object->m_value.array->end()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -10680,19 +10681,19 @@ class iter_impl */ pointer operator->() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { - assert(m_it.object_iterator != m_object->m_value.object->end()); + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { - assert(m_it.array_iterator != m_object->m_value.array->end()); + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -10725,7 +10726,7 @@ class iter_impl */ iter_impl& operator++() { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10768,7 +10769,7 @@ class iter_impl */ iter_impl& operator--() { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10806,7 +10807,7 @@ class iter_impl JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10842,7 +10843,7 @@ class iter_impl JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10890,7 +10891,7 @@ class iter_impl */ iter_impl& operator+=(difference_type i) { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10961,7 +10962,7 @@ class iter_impl */ difference_type operator-(const iter_impl& other) const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -10982,7 +10983,7 @@ class iter_impl */ reference operator[](difference_type n) const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); switch (m_object->m_type) { @@ -11013,7 +11014,7 @@ class iter_impl */ const typename object_t::key_type& key() const { - assert(m_object != nullptr); + JSON_ASSERT(m_object != nullptr); if (JSON_HEDLEY_LIKELY(m_object->is_object())) { @@ -11170,7 +11171,6 @@ class json_reverse_iterator : public std::reverse_iterator #include // all_of -#include // assert #include // isdigit #include // max #include // accumulate @@ -11953,7 +11953,7 @@ class json_pointer pos != std::string::npos; pos = reference_token.find_first_of('~', pos + 1)) { - assert(reference_token[pos] == '~'); + JSON_ASSERT(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or @@ -11988,7 +11988,7 @@ class json_pointer static void replace_substring(std::string& s, const std::string& f, const std::string& t) { - assert(not f.empty()); + JSON_ASSERT(not f.empty()); for (auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and @@ -12400,7 +12400,7 @@ class binary_writer */ explicit binary_writer(output_adapter_t adapter) : oa(adapter) { - assert(oa); + JSON_ASSERT(oa); } /*! @@ -13116,7 +13116,7 @@ class binary_writer bool prefix_required = true; if (use_type and not j.m_value.array->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) @@ -13160,7 +13160,7 @@ class binary_writer if (use_type and not j.m_value.binary->empty()) { - assert(use_count); + JSON_ASSERT(use_count); oa->write_character(to_char_type('$')); oa->write_character('U'); } @@ -13204,7 +13204,7 @@ class binary_writer bool prefix_required = true; if (use_type and not j.m_value.object->empty()) { - assert(use_count); + JSON_ASSERT(use_count); const CharType first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) @@ -13496,7 +13496,7 @@ class binary_writer // LCOV_EXCL_START default: - assert(false); + JSON_ASSERT(false); return 0ul; // LCOV_EXCL_STOP } @@ -13543,7 +13543,7 @@ class binary_writer // LCOV_EXCL_START default: - assert(false); + JSON_ASSERT(false); return; // LCOV_EXCL_STOP } @@ -13935,7 +13935,6 @@ class binary_writer #include // reverse, remove, fill, find, none_of #include // array -#include // assert #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t @@ -13952,7 +13951,6 @@ class binary_writer #include // array -#include // assert #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove @@ -14016,8 +14014,8 @@ struct diyfp // f * 2^e */ static diyfp sub(const diyfp& x, const diyfp& y) noexcept { - assert(x.e == y.e); - assert(x.f >= y.f); + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); return {x.f - y.f, x.e}; } @@ -14093,7 +14091,7 @@ struct diyfp // f * 2^e */ static diyfp normalize(diyfp x) noexcept { - assert(x.f != 0); + JSON_ASSERT(x.f != 0); while ((x.f >> 63u) == 0) { @@ -14112,8 +14110,8 @@ struct diyfp // f * 2^e { const int delta = x.e - target_exponent; - assert(delta >= 0); - assert(((x.f << delta) >> delta) == x.f); + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); return {x.f << delta, target_exponent}; } @@ -14135,8 +14133,8 @@ boundaries. template boundaries compute_boundaries(FloatType value) { - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // Convert the IEEE representation into a diyfp. // @@ -14416,18 +14414,18 @@ inline cached_power get_cached_power_for_binary_exponent(int e) // k = ceil((kAlpha - e - 1) * 0.30102999566398114) // for |e| <= 1500, but doesn't require floating-point operations. // NB: log_10(2) ~= 78913 / 2^18 - assert(e >= -1500); - assert(e <= 1500); + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); const int f = kAlpha - e - 1; const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; - assert(index >= 0); - assert(static_cast(index) < kCachedPowers.size()); + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); const cached_power cached = kCachedPowers[static_cast(index)]; - assert(kAlpha <= cached.e + e + 64); - assert(kGamma >= cached.e + e + 64); + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); return cached; } @@ -14495,10 +14493,10 @@ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, std::uint64_t rest, std::uint64_t ten_k) { - assert(len >= 1); - assert(dist <= delta); - assert(rest <= delta); - assert(ten_k > 0); + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); // <--------------------------- delta ----> // <---- dist ---------> @@ -14523,7 +14521,7 @@ inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t d and delta - rest >= ten_k and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) { - assert(buf[len - 1] != '0'); + JSON_ASSERT(buf[len - 1] != '0'); buf[len - 1]--; rest += ten_k; } @@ -14551,8 +14549,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // Grisu2 generates the digits of M+ from left to right and stops as soon as // V is in [M-,M+]. - assert(M_plus.e >= kAlpha); - assert(M_plus.e <= kGamma); + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) @@ -14573,7 +14571,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - assert(p1 > 0); + JSON_ASSERT(p1 > 0); std::uint32_t pow10; const int k = find_largest_pow10(p1, pow10); @@ -14609,7 +14607,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) // - assert(d <= 9); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) @@ -14696,7 +14694,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - assert(p2 > delta); + JSON_ASSERT(p2 > delta); int m = 0; for (;;) @@ -14707,7 +14705,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e // - assert(p2 <= (std::numeric_limits::max)() / 10); + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); p2 *= 10; const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e @@ -14716,7 +14714,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e // - assert(d <= 9); + JSON_ASSERT(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e @@ -14777,8 +14775,8 @@ JSON_HEDLEY_NON_NULL(1) inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) { - assert(m_plus.e == m_minus.e); - assert(m_plus.e == v.e); + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); // --------(-----------------------+-----------------------)-------- (A) // m- v m+ @@ -14839,8 +14837,8 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, "internal error: not enough precision"); - assert(std::isfinite(value)); - assert(value > 0); + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); // If the neighbors (and boundaries) of 'value' are always computed for double-precision // numbers, all float's can be recovered using strtod (and strtof). However, the resulting @@ -14876,8 +14874,8 @@ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* append_exponent(char* buf, int e) { - assert(e > -1000); - assert(e < 1000); + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); if (e < 0) { @@ -14929,8 +14927,8 @@ JSON_HEDLEY_RETURNS_NON_NULL inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp) { - assert(min_exp < 0); - assert(max_exp > 0); + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); const int k = len; const int n = len + decimal_exponent; @@ -14956,7 +14954,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent, // dig.its // len <= max_digits10 + 1 - assert(k > n); + JSON_ASSERT(k > n); std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); buf[n] = '.'; @@ -15014,7 +15012,7 @@ JSON_HEDLEY_RETURNS_NON_NULL char* to_chars(char* first, const char* last, FloatType value) { static_cast(last); // maybe unused - fix warning - assert(std::isfinite(value)); + JSON_ASSERT(std::isfinite(value)); // Use signbit(value) instead of (value < 0) since signbit works for -0. if (std::signbit(value)) @@ -15032,7 +15030,7 @@ char* to_chars(char* first, const char* last, FloatType value) return first; } - assert(last - first >= std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted @@ -15042,16 +15040,16 @@ char* to_chars(char* first, const char* last, FloatType value) int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); - assert(len <= std::numeric_limits::max_digits10); + JSON_ASSERT(len <= std::numeric_limits::max_digits10); // Format the buffer like printf("%.*g", prec, value) constexpr int kMinExp = -4; // Use digits10 here to increase compatibility with version 2. constexpr int kMaxExp = std::numeric_limits::digits10; - assert(last - first >= kMaxExp + 2); - assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); - assert(last - first >= std::numeric_limits::max_digits10 + 6); + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } @@ -15185,8 +15183,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); @@ -15213,8 +15211,8 @@ class serializer } // last element - assert(i != val.m_value.object->cend()); - assert(std::next(i) == val.m_value.object->cend()); + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); @@ -15255,7 +15253,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); @@ -15276,7 +15274,7 @@ class serializer } // last element - assert(not val.m_value.array->empty()); + JSON_ASSERT(not val.m_value.array->empty()); dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); @@ -15410,7 +15408,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } } @@ -15609,7 +15607,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } break; } @@ -15672,7 +15670,7 @@ class serializer } default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } } } @@ -15773,7 +15771,7 @@ class serializer } // spare 1 byte for '\0' - assert(n_chars < number_buffer.size() - 1); + JSON_ASSERT(n_chars < number_buffer.size() - 1); // jump to the end to generate the string from backward // so we later avoid reversing the result @@ -15849,9 +15847,9 @@ class serializer std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error - assert(len > 0); + JSON_ASSERT(len > 0); // check if buffer was large enough - assert(static_cast(len) < number_buffer.size()); + JSON_ASSERT(static_cast(len) < number_buffer.size()); // erase thousands separator if (thousands_sep != '\0') @@ -15859,7 +15857,7 @@ class serializer const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); + JSON_ASSERT((end - number_buffer.begin()) <= len); len = (end - number_buffer.begin()); } @@ -15939,7 +15937,7 @@ class serializer : (0xFFu >> type) & (byte); std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); - assert(index < 400); + JSON_ASSERT(index < 400); state = utf8d[index]; return state; } @@ -15951,7 +15949,7 @@ class serializer */ number_unsigned_t remove_sign(number_unsigned_t x) { - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE return x; // LCOV_EXCL_LINE } @@ -15966,7 +15964,7 @@ class serializer */ inline number_unsigned_t remove_sign(number_integer_t x) noexcept { - assert(x < 0 and x < (std::numeric_limits::max)()); + JSON_ASSERT(x < 0 and x < (std::numeric_limits::max)()); return static_cast(-(x + 1)) + 1; } @@ -16854,7 +16852,7 @@ class basic_json }; std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); - assert(object != nullptr); + JSON_ASSERT(object != nullptr); return object.release(); } @@ -17149,10 +17147,10 @@ class basic_json */ void assert_invariant() const noexcept { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); - assert(m_type != value_t::binary or m_value.binary != nullptr); + JSON_ASSERT(m_type != value_t::object or m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array or m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string or m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary or m_value.binary != nullptr); } public: @@ -17445,7 +17443,7 @@ class basic_json m_type = value_t::discarded; break; default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } assert_invariant(); } @@ -17845,8 +17843,8 @@ class basic_json std::is_same::value, int>::type = 0> basic_json(InputIT first, InputIT last) { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) @@ -19563,7 +19561,7 @@ class basic_json // const operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -19655,7 +19653,7 @@ class basic_json // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - assert(m_value.object->find(key) != m_value.object->end()); + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -21423,7 +21421,7 @@ class basic_json iterator insert_iterator(const_iterator pos, Args&& ... args) { iterator result(this); - assert(m_value.array != nullptr); + JSON_ASSERT(m_value.array != nullptr); auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); @@ -24188,7 +24186,7 @@ class basic_json // if there exists a parent it cannot be primitive default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE + JSON_ASSERT(false); // LCOV_EXCL_LINE } }; @@ -24723,6 +24721,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #endif // clean up +#undef JSON_ASSERT #undef JSON_INTERNAL_CATCH #undef JSON_CATCH #undef JSON_THROW From efcc826ecb9b55893397f749e5514316ba8629bb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 6 Jul 2020 12:37:39 +0200 Subject: [PATCH 39/49] :rotating_light: fix warning --- Makefile | 2 +- README.md | 4 ++-- test/src/unit-udt_macro.cpp | 46 ++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 8579fb5db..ff62d6e7d 100644 --- a/Makefile +++ b/Makefile @@ -253,7 +253,7 @@ pedantic_gcc: -Wmismatched-tags \ -Wmissing-attributes \ -Wmissing-braces \ - -Wmissing-declarations \ + -Wno-missing-declarations \ -Wmissing-field-initializers \ -Wmissing-include-dirs \ -Wmissing-profile \ diff --git a/README.md b/README.md index 6d21e2d85..3bb92d38b 100644 --- a/README.md +++ b/README.md @@ -886,7 +886,7 @@ The `to_json`/`from_json` functions for the `person` struct above can be created ```cpp namespace ns { - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age); + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age) } ``` @@ -901,7 +901,7 @@ namespace ns { int postcode; public: - NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode); + NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) }; } ``` diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index 4535ae5b5..a6b66e955 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -37,9 +37,9 @@ namespace persons class person_with_private_data { private: - std::string name; + std::string name = ""; int age = 0; - json metadata; + json metadata = nullptr; public: bool operator==(const person_with_private_data& rhs) const @@ -48,21 +48,21 @@ class person_with_private_data } person_with_private_data() = default; - person_with_private_data(std::string name, int age, json metadata) - : name(std::move(name)) - , age(age) - , metadata(std::move(metadata)) + person_with_private_data(std::string name_, int age_, json metadata_) + : name(std::move(name_)) + , age(age_) + , metadata(std::move(metadata_)) {} - NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata); + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata) }; class person_without_private_data_1 { public: - std::string name; - int age = 0; - json metadata; + std::string name = ""; + int age = 0; + json metadata = nullptr; bool operator==(const person_without_private_data_1& rhs) const { @@ -70,21 +70,21 @@ class person_without_private_data_1 } person_without_private_data_1() = default; - person_without_private_data_1(std::string name, int age, json metadata) - : name(std::move(name)) - , age(age) - , metadata(std::move(metadata)) + person_without_private_data_1(std::string name_, int age_, json metadata_) + : name(std::move(name_)) + , age(age_) + , metadata(std::move(metadata_)) {} - NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata); + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata) }; class person_without_private_data_2 { public: - std::string name; - int age = 0; - json metadata; + std::string name = ""; + int age = 0; + json metadata = nullptr; bool operator==(const person_without_private_data_2& rhs) const { @@ -92,14 +92,14 @@ class person_without_private_data_2 } person_without_private_data_2() = default; - person_without_private_data_2(std::string name, int age, json metadata) - : name(std::move(name)) - , age(age) - , metadata(std::move(metadata)) + person_without_private_data_2(std::string name_, int age_, json metadata_) + : name(std::move(name_)) + , age(age_) + , metadata(std::move(metadata_)) {} }; -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata); +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata) } // namespace persons TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T, From ba8174041e66038c4c02814ecbf2f8e062f2291c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 6 Jul 2020 12:52:48 +0200 Subject: [PATCH 40/49] :white_check_mark: add test case for JSON_ASSERT --- test/CMakeLists.txt | 1 + test/src/unit-assert_macro.cpp | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/src/unit-assert_macro.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8434f78d7..43af37d58 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -95,6 +95,7 @@ set(files src/unit-algorithms.cpp src/unit-allocator.cpp src/unit-alt-string.cpp + src/unit-assert_macro.cpp src/unit-bson.cpp src/unit-capacity.cpp src/unit-cbor.cpp diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp new file mode 100644 index 000000000..009a2b0b4 --- /dev/null +++ b/test/src/unit-assert_macro.cpp @@ -0,0 +1,50 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.8.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +/// global variable to record side effect of assert calls +int assert_counter = 0; + +/// set failure variable to true instead of calling assert(x) +#define JSON_ASSERT(x) if (!(x)) ++assert_counter; + +#include +using nlohmann::json; + +TEST_CASE("JSON_ASSERT(x)") +{ + const json j = {{"bar", 1}}; + CHECK(assert_counter == 0); + + // accessing non-exising key in const value would assert + j["foo"] == 1; + + CHECK(assert_counter == 1); +} \ No newline at end of file From 28ef87370be43ee4ea0d35c33097202c2d71fd2e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 6 Jul 2020 13:19:06 +0200 Subject: [PATCH 41/49] :rotating_light: fix warning --- Makefile | 2 ++ test/src/unit-assert_macro.cpp | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index ff62d6e7d..3d296baf9 100644 --- a/Makefile +++ b/Makefile @@ -97,6 +97,7 @@ doctest: # -Wno-exit-time-destructors: warning in json code triggered by NLOHMANN_JSON_SERIALIZE_ENUM # -Wno-float-equal: not all comparisons in the tests can be replaced by Approx # -Wno-keyword-macro: unit-tests use "#define private public" +# -Wno-missing-prototypes: for NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE # -Wno-padded: padding is nothing to warn about # -Wno-range-loop-analysis: items tests "for(const auto i...)" # -Wno-switch-enum -Wno-covered-switch-default: pedantic/contradicting warnings about switches @@ -113,6 +114,7 @@ pedantic_clang: -Wno-exit-time-destructors \ -Wno-float-equal \ -Wno-keyword-macro \ + -Wno-missing-prototypes \ -Wno-padded \ -Wno-range-loop-analysis \ -Wno-switch-enum -Wno-covered-switch-default \ diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp index 009a2b0b4..c5cd3a011 100644 --- a/test/src/unit-assert_macro.cpp +++ b/test/src/unit-assert_macro.cpp @@ -27,24 +27,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +// avoid warning when assert does not abort +#if defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif + #include "doctest_compatibility.h" /// global variable to record side effect of assert calls -int assert_counter = 0; +static int assert_counter; /// set failure variable to true instead of calling assert(x) -#define JSON_ASSERT(x) if (!(x)) ++assert_counter; +#define JSON_ASSERT(x) {if (!(x)) ++assert_counter; } #include using nlohmann::json; TEST_CASE("JSON_ASSERT(x)") { - const json j = {{"bar", 1}}; - CHECK(assert_counter == 0); + assert_counter = 0; + const json j = {{"bar", 1}}; + CHECK(assert_counter == 0); - // accessing non-exising key in const value would assert - j["foo"] == 1; + // accessing non-existing key in const value would assert + if (j["foo"] == 1) + { + CHECK(true); + } - CHECK(assert_counter == 1); -} \ No newline at end of file + CHECK(assert_counter == 1); +} From d019ddfcdb26ee08cd30bf85a10e2a37703f1c56 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 8 Jul 2020 12:25:53 +0200 Subject: [PATCH 42/49] :construction_worker: add code scanning --- .github/workflows/codeql-analysis.yml | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..5ee44a04e --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,54 @@ +name: "Code scanning - action" + +on: + push: + branches: [develop, ] + pull_request: + # The branches below must be a subset of the branches above + branches: [develop] + schedule: + - cron: '0 19 * * 1' + +jobs: + CodeQL-Build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 1b04092c5c724941e75ed7b993440cec80e318b8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 8 Jul 2020 12:38:46 +0200 Subject: [PATCH 43/49] :white_check_mark: fix test --- test/src/unit-assert_macro.cpp | 19 ++++++++++--------- test/src/unit-udt_macro.cpp | 12 ++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp index c5cd3a011..416cda236 100644 --- a/test/src/unit-assert_macro.cpp +++ b/test/src/unit-assert_macro.cpp @@ -45,15 +45,16 @@ using nlohmann::json; TEST_CASE("JSON_ASSERT(x)") { - assert_counter = 0; - const json j = {{"bar", 1}}; - CHECK(assert_counter == 0); - - // accessing non-existing key in const value would assert - if (j["foo"] == 1) + SECTION("basic_json(first, second)") { - CHECK(true); - } + assert_counter = 0; + CHECK(assert_counter == 0); - CHECK(assert_counter == 1); + json::iterator it; + json j; + + CHECK_THROWS_WITH_AS(json(it, j.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator); + + CHECK(assert_counter == 1); + } } diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index a6b66e955..90d5e1cc4 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -60,9 +60,9 @@ class person_with_private_data class person_without_private_data_1 { public: - std::string name = ""; - int age = 0; - json metadata = nullptr; + std::string name = ""; + int age = 0; + json metadata = nullptr; bool operator==(const person_without_private_data_1& rhs) const { @@ -82,9 +82,9 @@ class person_without_private_data_1 class person_without_private_data_2 { public: - std::string name = ""; - int age = 0; - json metadata = nullptr; + std::string name = ""; + int age = 0; + json metadata = nullptr; bool operator==(const person_without_private_data_2& rhs) const { From e3e9ccfc027c26b75234ce1b681bb93929035563 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 8 Jul 2020 14:02:28 +0200 Subject: [PATCH 44/49] :ambulance: fix regression from #2181 --- include/nlohmann/json.hpp | 12 ++++++------ single_include/nlohmann/json.hpp | 12 ++++++------ test/src/unit-regression.cpp | 9 +++++++++ test/src/unit-udt_macro.cpp | 12 ++++++------ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 1dedb809b..8a3957a60 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3785,7 +3785,7 @@ class basic_json template::value and not std::is_same::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType && default_value) const + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -3797,7 +3797,7 @@ class basic_json return *it; } - return std::move(default_value); + return default_value; } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); @@ -3809,7 +3809,7 @@ class basic_json */ string_t value(const typename object_t::key_type& key, const char* default_value) const { - return value(key, std::move(string_t(default_value))); + return value(key, string_t(default_value)); } /*! @@ -3857,7 +3857,7 @@ class basic_json */ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, ValueType && default_value) const + ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -3869,7 +3869,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return std::move(default_value); + return default_value; } } @@ -3883,7 +3883,7 @@ class basic_json JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { - return value(ptr, std::move(string_t(default_value))); + return value(ptr, string_t(default_value)); } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 87222900b..05c3c925e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19715,7 +19715,7 @@ class basic_json template::value and not std::is_same::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType && default_value) const + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19727,7 +19727,7 @@ class basic_json return *it; } - return std::move(default_value); + return default_value; } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); @@ -19739,7 +19739,7 @@ class basic_json */ string_t value(const typename object_t::key_type& key, const char* default_value) const { - return value(key, std::move(string_t(default_value))); + return value(key, string_t(default_value)); } /*! @@ -19787,7 +19787,7 @@ class basic_json */ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, ValueType && default_value) const + ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19799,7 +19799,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return std::move(default_value); + return default_value; } } @@ -19813,7 +19813,7 @@ class basic_json JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { - return value(ptr, std::move(string_t(default_value))); + return value(ptr, string_t(default_value)); } /*! diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index e5cd0337b..3f9d9c26d 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1945,6 +1945,15 @@ TEST_CASE("regression tests") ) ); } + + SECTION("PR #2181 - regression bug with lvalue") + { + // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060 + json j{{"x", "test"}}; + std::string defval = "default value"; + auto val = j.value("x", defval); + auto val2 = j.value("y", defval); + } } #if not defined(JSON_NOEXCEPTION) diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index a6b66e955..90d5e1cc4 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -60,9 +60,9 @@ class person_with_private_data class person_without_private_data_1 { public: - std::string name = ""; - int age = 0; - json metadata = nullptr; + std::string name = ""; + int age = 0; + json metadata = nullptr; bool operator==(const person_without_private_data_1& rhs) const { @@ -82,9 +82,9 @@ class person_without_private_data_1 class person_without_private_data_2 { public: - std::string name = ""; - int age = 0; - json metadata = nullptr; + std::string name = ""; + int age = 0; + json metadata = nullptr; bool operator==(const person_without_private_data_2& rhs) const { From f774a32d2baccc06d744dd8f794fca53babb7fd8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 8 Jul 2020 14:47:22 +0200 Subject: [PATCH 45/49] :white_check_mark: fix test --- test/src/unit-assert_macro.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/src/unit-assert_macro.cpp b/test/src/unit-assert_macro.cpp index 416cda236..06aa07704 100644 --- a/test/src/unit-assert_macro.cpp +++ b/test/src/unit-assert_macro.cpp @@ -43,6 +43,8 @@ static int assert_counter; #include using nlohmann::json; +// the test assumes exceptions to work +#if not defined(JSON_NOEXCEPTION) TEST_CASE("JSON_ASSERT(x)") { SECTION("basic_json(first, second)") @@ -53,8 +55,11 @@ TEST_CASE("JSON_ASSERT(x)") json::iterator it; json j; + // in case assertions do not abort execution, an exception is thrown CHECK_THROWS_WITH_AS(json(it, j.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator); + // check that assertion actually happened CHECK(assert_counter == 1); } } +#endif From c7d18c16259048878ab566c30aea2078a94b58fe Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 8 Aug 2019 14:36:27 -0700 Subject: [PATCH 46/49] cmake: Generate and install a pkg-config file. The meson builds install a pkg-config file, but the cmake builds don't. This adds a pkg-config file to the cmake builds that is functionally equivalent to the one generated from meson. --- CMakeLists.txt | 12 +++++++++++- cmake/pkg-config.pc.in | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 cmake/pkg-config.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index f7ba4fa38..372a7a03a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1) ## PROJECT ## name and version ## -project(nlohmann_json VERSION 3.8.0 LANGUAGES CXX) +project(nlohmann_json VERSION 3.8.0 DESCRIPTION "JSON for Modern C++" LANGUAGES CXX) ## ## INCLUDE @@ -79,6 +79,12 @@ if (MSVC) ) endif() +# Install a pkg-config file, so other tools can find this. +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" +) + ## ## TESTS ## create and configure the unit test target @@ -139,4 +145,8 @@ endif() NAMESPACE ${PROJECT_NAME}:: DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR} ) + install( + FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION lib/pkgconfig + ) endif() diff --git a/cmake/pkg-config.pc.in b/cmake/pkg-config.pc.in new file mode 100644 index 000000000..680f10c26 --- /dev/null +++ b/cmake/pkg-config.pc.in @@ -0,0 +1,4 @@ +Name: ${PROJECT_NAME} +Description: ${PROJECT_DESCRIPTION} +Version: ${PROJECT_VERSION} +Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR} From 251fce819c64b356d039f1777740ab9c0489d495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Wed, 8 Jul 2020 17:10:51 -0300 Subject: [PATCH 47/49] README: add explanation on how to use pkg-config. --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 3bb92d38b..41d3145cb 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ - [Integration](#integration) - [CMake](#cmake) - [Package Managers](#package-managers) + - [Pkg-config](#pkg-config) - [Examples](#examples) - [JSON as first-class data type](#json-as-first-class-data-type) - [Serialization / Deserialization](#serialization--deserialization) @@ -230,6 +231,20 @@ Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if If you are using [`wsjcpp`](https://wsjcpp.org), you can use the command `wsjcpp install "https://github.com/nlohmann/json:develop"` to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch. +### Pkg-config + +If you are using bare Makefiles, you can use `pkg-config` to generate the include flags that point to where the library is installed: + +```sh +pkg-config nlohmann_json --cflags +``` + +Users of the Meson build system will also be able to use a system wide library, which will be found by `pkg-config`: + +```meson +json = dependency('nlohmann_json', required: true) +``` + ## Examples Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5338e282d1d02bed389d852dd670d98d.html#a5338e282d1d02bed389d852dd670d98d)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)). From d740622f961a07d73fa2f6e1f61841812c998b1f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 9 Jul 2020 13:23:33 +0200 Subject: [PATCH 48/49] :pencil: add documentation for macros --- doc/mkdocs/docs/features/arbitrary_types.md | 37 ++++++++++++++ doc/mkdocs/docs/features/macros.md | 55 +++++++++++++++++++++ doc/mkdocs/docs/home/exceptions.md | 18 +++++++ doc/mkdocs/mkdocs.yml | 1 + 4 files changed, 111 insertions(+) create mode 100644 doc/mkdocs/docs/features/macros.md diff --git a/doc/mkdocs/docs/features/arbitrary_types.md b/doc/mkdocs/docs/features/arbitrary_types.md index 3d2383174..7bd7adf7d 100644 --- a/doc/mkdocs/docs/features/arbitrary_types.md +++ b/doc/mkdocs/docs/features/arbitrary_types.md @@ -81,6 +81,43 @@ Some important things: * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. +## Simplify your life with macros + +If you just want to serialize/deserialize some structs, the `to_json`/`from_json` functions can be a lot of boilerplate. + +There are two macros to make your life easier as long as you (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object: + +- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the namespace of the class/struct to create code for. +- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside of the class/struct to create code for. This macro can also access private members. + +In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. + +??? example + + The `to_json`/`from_json` functions for the `person` struct above can be created with: + + ```cpp + namespace ns { + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age) + } + ``` + + Here is an example with private members, where `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is needed: + + ```cpp + namespace ns { + class address { + private: + std::string street; + int housenumber; + int postcode; + + public: + NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) + }; + } + ``` + ## How do I convert third-party types? This requires a bit more advanced technique. But first, let's see how this conversion mechanism works: diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md new file mode 100644 index 000000000..7147be7e1 --- /dev/null +++ b/doc/mkdocs/docs/features/macros.md @@ -0,0 +1,55 @@ +# Supported Macros + +Some aspects of the library can be configured by defining preprocessor macros before including the `json.hpp` header. + +## `JSON_CATCH_USER(exception)` + +This macro overrides `#!cpp catch` calls inside the library. The argument is the type of the exception to catch. As of version 3.8.0, the library only catches `std::out_of_range` exceptions internally to rethrow them as [`json::out_of_range`](../home/exceptions.md#out-of-range) exceptions. The macro is always followed by a scope. + +See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example. + +## `JSON_NOEXCEPTION` + +Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. +When defining `JSON_NOEXCEPTION`, `#!cpp try` is replaced by `#!cpp if (true)`, +`#!cpp catch` is replaced by `#!cpp if (false)`, and `#!cpp throw` is replaced by `#!cpp std::abort()`. + +The same effect is achieved by setting the compiler flag `-fno-exceptions`. + +## `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK` + +When defined, the library will not create a compile error when a known unsupported compiler is detected. This allows to use the library with compilers that do not fully support C++11 and may only work if unsupported features are not used. + +## `JSON_THROW_USER(exception)` + +This macro overrides `#!cpp throw` calls inside the library. The argument is the exception to be thrown. Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. + +See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example. + +## `JSON_TRY_USER` + +This macro overrides `#!cpp try` calls inside the library. It has no arguments and is always followed by a scope. + +See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example. + +## `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)` + +This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object. + +The macro is to be defined inside of the class/struct to create code for. Unlike [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](#nlohmann_define_type_non_intrusivetype-member), it can access private members. +The first parameter is the name of the class/struct, and all remaining parameters name the members. + +See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example. + +## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)` + +This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object. + +The macro is to be defined inside of the namespace of the class/struct to create code for. Private members cannot be accessed. Use [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](#nlohmann_define_type_intrusivetype-member) in these scenarios. +The first parameter is the name of the class/struct, and all remaining parameters name the members. + +See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example. + +## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` + +This macro simplifies the serialization/deserialization of enum types. See [Specializing enum conversion](enum_conversion.md) for more information. diff --git a/doc/mkdocs/docs/home/exceptions.md b/doc/mkdocs/docs/home/exceptions.md index 92dfb43b7..d7430ccc6 100644 --- a/doc/mkdocs/docs/home/exceptions.md +++ b/doc/mkdocs/docs/home/exceptions.md @@ -32,6 +32,24 @@ Exceptions are used widely within the library. They can, however, be switched of Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. +??? example + + The code below switches off exceptions and creates a log entry with a detailed error message in case of errors. + + ```cpp + #include + + #define JSON_TRY_USER if(true) + #define JSON_CATCH_USER(exception) if(false) + #define JSON_THROW_USER(exception) \ + {std::clog << "Error in " << __FILE__ << ":" << __LINE__ \ + << " (function " << __FUNCTION__ << ") - " \ + << (exception).what() << std::endl; \ + std::abort();} + + #include + ``` + ## Parse errors This exception is thrown by the library when a parse error occurs. Parse errors diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 5d6e3b4e3..9169e4f19 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -49,6 +49,7 @@ nav: - features/json_patch.md - features/merge_patch.md - features/enum_conversion.md + - features/macros.md - Parsing: - features/parsing/index.md - features/parsing/parse_exceptions.md From cbafed349493dd8b072b5c1c5ac7f7c0ef0ed7df Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 10 Jul 2020 12:53:48 +0200 Subject: [PATCH 49/49] :arrow_down: require CMake 3.1 --- CMakeLists.txt | 2 +- cmake/pkg-config.pc.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 372a7a03a..5df3f99f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1) ## PROJECT ## name and version ## -project(nlohmann_json VERSION 3.8.0 DESCRIPTION "JSON for Modern C++" LANGUAGES CXX) +project(nlohmann_json VERSION 3.8.0 LANGUAGES CXX) ## ## INCLUDE diff --git a/cmake/pkg-config.pc.in b/cmake/pkg-config.pc.in index 680f10c26..3541abf0b 100644 --- a/cmake/pkg-config.pc.in +++ b/cmake/pkg-config.pc.in @@ -1,4 +1,4 @@ Name: ${PROJECT_NAME} -Description: ${PROJECT_DESCRIPTION} +Description: JSON for Modern C++ Version: ${PROJECT_VERSION} Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}