From 78ddf2bcf821f7a342099b927084bdc0874354d0 Mon Sep 17 00:00:00 2001 From: Giovanni Cerretani Date: Mon, 3 Jan 2022 13:57:51 +0100 Subject: [PATCH 001/110] fix _MSC_VER version to check for std::filesystem (#3240) --- include/nlohmann/detail/macro_scope.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index b2843f067..bcb70e911 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -70,7 +70,7 @@ #endif // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1940 + #if defined(_MSC_VER) && _MSC_VER < 1914 #undef JSON_HAS_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #endif diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cb27e0581..bfa5c823c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2367,7 +2367,7 @@ using is_detected_convertible = #endif // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1940 + #if defined(_MSC_VER) && _MSC_VER < 1914 #undef JSON_HAS_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #endif From 9e89c2fdb5dffb400413289cdef11b2b79bbdad8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 4 Jan 2022 09:25:41 +0100 Subject: [PATCH 002/110] :recycle: remove stringstream (#3244) --- include/nlohmann/detail/output/serializer.hpp | 23 +++++++++++++------ single_include/nlohmann/json.hpp | 23 +++++++++++++------ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 7cce7d5c7..cc63ab1d3 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -10,7 +10,6 @@ #include // numeric_limits #include // string, char_traits #include // setfill, setw -#include // stringstream #include // is_same #include // move @@ -501,9 +500,7 @@ class serializer { case error_handler_t::strict: { - std::stringstream ss; - ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType())); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + hex_bytes(byte | 0), BasicJsonType())); } case error_handler_t::ignore: @@ -595,9 +592,7 @@ class serializer { case error_handler_t::strict: { - std::stringstream ss; - ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast(s.back()) | 0); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + hex_bytes(static_cast(s.back() | 0)), BasicJsonType())); } case error_handler_t::ignore: @@ -664,6 +659,20 @@ class serializer } } + /*! + * @brief convert a byte to a uppercase hex representation + * @param[in] byte byte to represent + * @return representation ("00".."FF") + */ + inline std::string hex_bytes(std::uint8_t byte) + { + std::string result = "FF"; + constexpr const char* nibble_to_hex = "0123456789ABCDEF"; + result[0] = nibble_to_hex[byte / 16]; + result[1] = nibble_to_hex[byte % 16]; + return result; + } + // templates to avoid warnings about useless casts template ::value, int> = 0> bool is_negative_number(NumberType x) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bfa5c823c..e573adc13 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -14935,7 +14935,6 @@ class binary_writer #include // numeric_limits #include // string, char_traits #include // setfill, setw -#include // stringstream #include // is_same #include // move @@ -16544,9 +16543,7 @@ class serializer { case error_handler_t::strict: { - std::stringstream ss; - ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0); - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType())); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + hex_bytes(byte | 0), BasicJsonType())); } case error_handler_t::ignore: @@ -16638,9 +16635,7 @@ class serializer { case error_handler_t::strict: { - std::stringstream ss; - ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast(s.back()) | 0); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + hex_bytes(static_cast(s.back() | 0)), BasicJsonType())); } case error_handler_t::ignore: @@ -16707,6 +16702,20 @@ class serializer } } + /*! + * @brief convert a byte to a uppercase hex representation + * @param[in] byte byte to represent + * @return representation ("00".."FF") + */ + inline std::string hex_bytes(std::uint8_t byte) + { + std::string result = "FF"; + constexpr const char* nibble_to_hex = "0123456789ABCDEF"; + result[0] = nibble_to_hex[byte / 16]; + result[1] = nibble_to_hex[byte % 16]; + return result; + } + // templates to avoid warnings about useless casts template ::value, int> = 0> bool is_negative_number(NumberType x) From a8e86ba94808dd1229cd6232606f9945e1d0081e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 4 Jan 2022 15:02:39 +0100 Subject: [PATCH 003/110] :memo: improve documentation InputType and IteratorType (#3246) --- doc/mkdocs/docs/api/basic_json/accept.md | 6 +++++- doc/mkdocs/docs/api/basic_json/parse.md | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/api/basic_json/accept.md b/doc/mkdocs/docs/api/basic_json/accept.md index 8794c585d..bbbac053a 100644 --- a/doc/mkdocs/docs/api/basic_json/accept.md +++ b/doc/mkdocs/docs/api/basic_json/accept.md @@ -32,10 +32,14 @@ Unlike the [`parse`](parse.md) function, this function neither throws an excepti - a `FILE` pointer - a C-style array of characters - a pointer to a null-terminated string of single byte characters + - a `std::string` - an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators. `IteratorType` -: a compatible iterator type +: a compatible iterator type, for instance. + + - a pair of `std::string::iterator` or `std::vector::iterator` + - a pair of pointers such as `ptr` and `ptr + len` ## Parameters diff --git a/doc/mkdocs/docs/api/basic_json/parse.md b/doc/mkdocs/docs/api/basic_json/parse.md index 95d876b0a..92808cd6b 100644 --- a/doc/mkdocs/docs/api/basic_json/parse.md +++ b/doc/mkdocs/docs/api/basic_json/parse.md @@ -31,6 +31,7 @@ static basic_json parse(IteratorType first, IteratorType last, - a `FILE` pointer - a C-style array of characters - a pointer to a null-terminated string of single byte characters + - a `std::string` - an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators. `IteratorType` From 4fc7b3dc7c85563bd38434976d31a349ef27d801 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 4 Jan 2022 15:20:50 +0100 Subject: [PATCH 004/110] :wrench: adjust docset generation script --- doc/docset/Makefile | 1 - doc/docset/docset.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/docset/Makefile b/doc/docset/Makefile index e538661ec..fcefa413a 100644 --- a/doc/docset/Makefile +++ b/doc/docset/Makefile @@ -13,7 +13,6 @@ JSON_for_Modern_C++.docset: Info.plist docSet.sql # remove "JSON for Modern C++" from page titles find JSON_for_Modern_C++.docset/Contents/Resources/Documents -type f -exec gsed -i 's| - JSON for Modern C++||' {} + # clean up - rm JSON_for_Modern_C++.docset/Contents/Resources/Documents/hooks.py rm JSON_for_Modern_C++.docset/Contents/Resources/Documents/sitemap.* # generate index sqlite3 JSON_for_Modern_C++.docset/Contents/Resources/docSet.dsidx < docSet.sql diff --git a/doc/docset/docset.json b/doc/docset/docset.json index f8d478302..96f7cd259 100644 --- a/doc/docset/docset.json +++ b/doc/docset/docset.json @@ -1,6 +1,6 @@ { "name": "JSON for Modern C++", - "version": "3.10.0", + "version": "3.10.5", "archive": "JSON_for_Modern_C++.tgz", "author": { "name": "Niels Lohmann", From 6cd68ebd12495dd711ca9089f7e7193f3f9ae077 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 5 Jan 2022 09:51:29 +0100 Subject: [PATCH 005/110] :memo: add documentation for JSON Lines (#3247) --- doc/examples/json_lines.cpp | 22 +++++++++ doc/examples/json_lines.output | 4 ++ .../docs/features/parsing/json_lines.md | 49 +++++++++++++++++++ doc/mkdocs/mkdocs.yml | 1 + test/src/unit-deserialization.cpp | 42 ++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 doc/examples/json_lines.cpp create mode 100644 doc/examples/json_lines.output create mode 100644 doc/mkdocs/docs/features/parsing/json_lines.md diff --git a/doc/examples/json_lines.cpp b/doc/examples/json_lines.cpp new file mode 100644 index 000000000..233c81a4a --- /dev/null +++ b/doc/examples/json_lines.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +using json = nlohmann::json; + +int main() +{ + // JSON Lines (see https://jsonlines.org) + std::stringstream input; + input << R"({"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]} +{"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]} +{"name": "May", "wins": []} +{"name": "Deloise", "wins": [["three of a kind", "5♣"]]} +)"; + + std::string line; + while (std::getline(input, line)) + { + std::cout << json::parse(line) << std::endl; + } +} diff --git a/doc/examples/json_lines.output b/doc/examples/json_lines.output new file mode 100644 index 000000000..1b4122480 --- /dev/null +++ b/doc/examples/json_lines.output @@ -0,0 +1,4 @@ +{"name":"Gilbert","wins":[["straight","7♣"],["one pair","10♥"]]} +{"name":"Alexa","wins":[["two pair","4♠"],["two pair","9♠"]]} +{"name":"May","wins":[]} +{"name":"Deloise","wins":[["three of a kind","5♣"]]} diff --git a/doc/mkdocs/docs/features/parsing/json_lines.md b/doc/mkdocs/docs/features/parsing/json_lines.md new file mode 100644 index 000000000..9542cb0c5 --- /dev/null +++ b/doc/mkdocs/docs/features/parsing/json_lines.md @@ -0,0 +1,49 @@ +# JSON Lines + +The [JSON Lines](https://jsonlines.org) format is a text format of newline-delimited JSON. In particular: + +1. The input must be UTF-8 encoded. +2. Every line must be a valid JSON value. +3. The line separator must be `\n`. As `\r` is silently ignored, `\r\n` is also supported. +4. The final character may be `\n`, but is not required to be one. + +!!! example "JSON Text example" + + ```json + {"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]} + {"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]} + {"name": "May", "wins": []} + {"name": "Deloise", "wins": [["three of a kind", "5♣"]]} + ``` + +JSON Lines input with more than one value is treated as invalid JSON by the [`parse`](../../api/basic_json/parse.md) or +[`accept`](../../api/basic_json/accept.md) functions. The process it line by line, functions like +[`std::getline`](https://en.cppreference.com/w/cpp/string/basic_string/getline) can be used: + +!!! example "Example: Parse JSON Text input line by line" + + The example below demonstrates how JSON Lines can be processed. + + ```cpp + --8<-- "examples/json_lines.cpp" + ``` + + Output: + + ```json + --8<-- "examples/json_lines.output" + ``` + +!!! warning "Note" + + Using [`operator>>`](../../api/basic_json/operator_gtgt.md) like + + ```cpp + json j; + while (input >> j) + { + std::cout << j << std::endl; + } + ``` + + with a JSON Lines input does not work, because the parser will try to parse one value after the last one. diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 542d4f173..618f7344b 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -60,6 +60,7 @@ nav: - features/object_order.md - Parsing: - features/parsing/index.md + - features/parsing/json_lines.md - features/parsing/parse_exceptions.md - features/parsing/parser_callbacks.md - features/parsing/sax_interface.md diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index bb24241ce..45a8c0e6b 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -1065,6 +1065,48 @@ TEST_CASE("deserialization") "start_array()" })); } + + SECTION("JSON Lines") + { + SECTION("Example file") + { + std::stringstream ss; + ss << R"({"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]} + {"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]} + {"name": "May", "wins": []} + {"name": "Deloise", "wins": [["three of a kind", "5♣"]]} +)"; + + std::string line; + int object_count = 0; + while (std::getline(ss, line)) + { + ++object_count; + CHECK(json::accept(line)); + } + + CHECK(object_count == 4); + } + + SECTION("Example file without trailing newline") + { + std::stringstream ss; + ss << R"({"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]} + {"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]} + {"name": "May", "wins": []} + {"name": "Deloise", "wins": [["three of a kind", "5♣"]]})"; + + std::string line; + int object_count = 0; + while (std::getline(ss, line)) + { + ++object_count; + CHECK(json::accept(line)); + } + + CHECK(object_count == 4); + } + } } TEST_CASE_TEMPLATE("deserialization of different character types (ASCII)", T, From b785783440c48b8d2981e09509db4ef46131f0fd Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Wed, 5 Jan 2022 14:35:14 +0200 Subject: [PATCH 006/110] Fix typo (#3249) --- doc/mkdocs/docs/api/basic_json/is_discarded.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/api/basic_json/is_discarded.md b/doc/mkdocs/docs/api/basic_json/is_discarded.md index 405ae6c11..6de31c937 100644 --- a/doc/mkdocs/docs/api/basic_json/is_discarded.md +++ b/doc/mkdocs/docs/api/basic_json/is_discarded.md @@ -44,7 +44,7 @@ Constant. !!! note When a value is discarded by a callback function (see [`parser_callback_t`](parser_callback_t.md)) during parsing, - then it is removed when it is part of a structured value. For instance, if the second value of an array is discared, + then it is removed when it is part of a structured value. For instance, if the second value of an array is discarded, instead of `#!json [null, discarded, false]`, the array `#!json [null, false]` is returned. Only if the top-level value is discarded, the return value of the `parse` call is discarded. From 4b6220acf2b6780350df36df12afc18eeb24a50c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 5 Jan 2022 21:18:27 +0100 Subject: [PATCH 007/110] :pencil2: fix typo --- doc/mkdocs/docs/features/parsing/json_lines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/features/parsing/json_lines.md b/doc/mkdocs/docs/features/parsing/json_lines.md index 9542cb0c5..69c2163b7 100644 --- a/doc/mkdocs/docs/features/parsing/json_lines.md +++ b/doc/mkdocs/docs/features/parsing/json_lines.md @@ -17,7 +17,7 @@ The [JSON Lines](https://jsonlines.org) format is a text format of newline-delim ``` JSON Lines input with more than one value is treated as invalid JSON by the [`parse`](../../api/basic_json/parse.md) or -[`accept`](../../api/basic_json/accept.md) functions. The process it line by line, functions like +[`accept`](../../api/basic_json/accept.md) functions. To process it line by line, functions like [`std::getline`](https://en.cppreference.com/w/cpp/string/basic_string/getline) can be used: !!! example "Example: Parse JSON Text input line by line" From 6d8d043adde8c95589f6e65e1fe228cb1f0d8419 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 5 Jan 2022 21:21:46 +0100 Subject: [PATCH 008/110] :recycle: make function static --- 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 cc63ab1d3..e475ef959 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -664,7 +664,7 @@ class serializer * @param[in] byte byte to represent * @return representation ("00".."FF") */ - inline std::string hex_bytes(std::uint8_t byte) + static std::string hex_bytes(std::uint8_t byte) { std::string result = "FF"; constexpr const char* nibble_to_hex = "0123456789ABCDEF"; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e573adc13..92308c557 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -16707,7 +16707,7 @@ class serializer * @param[in] byte byte to represent * @return representation ("00".."FF") */ - inline std::string hex_bytes(std::uint8_t byte) + static std::string hex_bytes(std::uint8_t byte) { std::string result = "FF"; constexpr const char* nibble_to_hex = "0123456789ABCDEF"; From ef556019be51ba3db32854e2a491b4ccb540a2c8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 9 Jan 2022 14:32:38 +0100 Subject: [PATCH 009/110] :memo: overwork documentation --- .../operator_literal_json_pointer.md | 4 + doc/mkdocs/docs/api/json_pointer/index.md | 1 + doc/mkdocs/docs/features/json_pointer.md | 122 +++++++++++- doc/mkdocs/docs/integration/cmake.md | 174 ++++++++++-------- doc/mkdocs/docs/integration/example.cpp | 3 +- doc/mkdocs/docs/integration/index.md | 10 +- .../docs/integration/package_managers.md | 73 ++++---- 7 files changed, 265 insertions(+), 122 deletions(-) diff --git a/doc/mkdocs/docs/api/basic_json/operator_literal_json_pointer.md b/doc/mkdocs/docs/api/basic_json/operator_literal_json_pointer.md index 38c957e61..897ac07cd 100644 --- a/doc/mkdocs/docs/api/basic_json/operator_literal_json_pointer.md +++ b/doc/mkdocs/docs/api/basic_json/operator_literal_json_pointer.md @@ -43,6 +43,10 @@ Linear. --8<-- "examples/operator_literal_json_pointer.output" ``` +## See also + +- [json_pointer](../json_pointer/index.md) - type to represent JSON Pointers + ## Version history - Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/json_pointer/index.md b/doc/mkdocs/docs/api/json_pointer/index.md index 6ef9435fb..d95ed0ffb 100644 --- a/doc/mkdocs/docs/api/json_pointer/index.md +++ b/doc/mkdocs/docs/api/json_pointer/index.md @@ -29,6 +29,7 @@ are the base for JSON patches. ## See also +- [operator""_json_pointer](../basic_json/operator_literal_json_pointer.md) - user-defined string literal for JSON pointers - [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) ## Version history diff --git a/doc/mkdocs/docs/features/json_pointer.md b/doc/mkdocs/docs/features/json_pointer.md index 8f5d60ec7..a7861ec7d 100644 --- a/doc/mkdocs/docs/features/json_pointer.md +++ b/doc/mkdocs/docs/features/json_pointer.md @@ -1,19 +1,123 @@ # JSON Pointer -The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address structured values. +## Introduction + +The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address +structured values. A JSON Pointer is a string that identifies a specific value withing a JSON document. + +Consider the following JSON document + +```json +{ + "array": ["A", "B", "C"], + "nested": { + "one": 1, + "two": 2, + "three": [true, false] + } +} +``` + +Then every value inside the JSON document can be idientified as follows: + +| JSON Pointer | JSON value | +|-------------------|----------------------------------------------------------------------------------| +| `/` | `#!json {"array":["A","B","C"],"nested":{"one":1,"two":2,"three":[true,false]}}` | +| `/array` | `#!json ["A","B","C"]` | +| `/array/0` | `#!json A` | +| `/array/1` | `#!json B` | +| `/array/2` | `#!json C` | +| `/nested` | `#!json {"one":1,"two":2,"three":[true,false]}` | +| `/nested/one` | `#!json 1` | +| `/nested/two` | `#!json 2` | +| `/nested/three` | `#!json [true,false]` | +| `/nested/three/0` | `#!json true` | +| `/nested/three/1` | `#!json false` | + +## JSON Pointer creation + +JSON Pointers can be created from a string: ```cpp -// a JSON value -json j_original = R"({ - "baz": ["one", "two", "three"], - "foo": "bar" -})"_json; +json::json_pointer p = "/nested/one"; +``` -// access members with a JSON pointer (RFC 6901) -j_original["/baz/1"_json_pointer]; -// "two" +Furthermore, a user-defined string literal can be used to achieve the same result: + +```cpp +auto p = "/nested/one"_json_pointer; +``` + +The escaping rules of [RFC 6901](https://tools.ietf.org/html/rfc6901) are implemented. See the +[constructor documentation](../api/json_pointer/json_pointer.md) for more information. + +## Value access + +JSON Pointers can be used in the [`at`](../api/basic_json/at.md), [`operator[]`](../api/basic_json/operator%5B%5D.md), +and [`value`](../api/basic_json/value.md) functions just like object keys or array indices. + +```cpp +// the JSON value from above +auto j = json::parse(R"({ + "array": ["A", "B", "C"], + "nested": { + "one": 1, + "two": 2, + "three": [true, false] + } +})"); + +// access values +auto val = j["/"_json_pointer]; // {"array":["A","B","C"],...} +auto val1 = j["/nested/one"_json_pointer]; // 1 +auto val2 = j.at[json::json_pointer("/nested/three/1")]; // false +auto val3 = j.value[json::json_pointer("/nested/four", 0)]; // 0 +``` + +## Flatten / unflatten + +The library implements a function [`flatten`](../api/basic_json/flatten.md) to convert any JSON document into a JSON +object where each key is a JSON Pointer and each value is a primitive JSON value (i.e., a string, boolean, number, or +null). + +```cpp +// the JSON value from above +auto j = json::parse(R"({ + "array": ["A", "B", "C"], + "nested": { + "one": 1, + "two": 2, + "three": [true, false] + } +})"); + +// create flattened value +auto j_flat = j.flatten(); +``` + +The resulting value `j_flat` is: + +```json +{ + "/array/0": "A", + "/array/1": "B", + "/array/2": "C", + "/nested/one": 1, + "/nested/two": 2, + "/nested/three/0": true, + "/nested/three/1": false +} +``` + +The reverse function, [`unflatten`](../api/basic_json/unflatten.md) recreates the original value. + +```cpp +auto j_original = j_flat.unflatten(); ``` ## See also - Class [`json_pointer`](../api/json_pointer/index.md) +- Function [`flatten`](../api/basic_json/flatten.md) +- Function [`unflatten`](../api/basic_json/unflatten.md) +- [JSON Patch](json_patch.md) diff --git a/doc/mkdocs/docs/integration/cmake.md b/doc/mkdocs/docs/integration/cmake.md index 9f1ecc95a..44aeb26e1 100644 --- a/doc/mkdocs/docs/integration/cmake.md +++ b/doc/mkdocs/docs/integration/cmake.md @@ -2,102 +2,124 @@ ## Integration -You can also use the `nlohmann_json::nlohmann_json` interface target in CMake. This target populates the appropriate usage requirements for `INTERFACE_INCLUDE_DIRECTORIES` to point to the appropriate include directories and `INTERFACE_COMPILE_FEATURES` for the necessary C++11 flags. +You can use the `nlohmann_json::nlohmann_json` interface target in CMake. This target populates the appropriate usage +requirements for [`INTERFACE_INCLUDE_DIRECTORIES`](https://cmake.org/cmake/help/latest/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES.html) +to point to the appropriate include directories and [`INTERFACE_COMPILE_FEATURES`](https://cmake.org/cmake/help/latest/prop_tgt/INTERFACE_COMPILE_FEATURES.html) +for the necessary C++11 flags. ### External -To use this library from a CMake project, you can locate it directly with `find_package()` and use the namespaced imported target from the generated package configuration: +To use this library from a CMake project, you can locate it directly with [`find_package()`](https://cmake.org/cmake/help/latest/command/find_package.html) +and use the namespaced imported target from the generated package configuration: -```cmake -# CMakeLists.txt -find_package(nlohmann_json 3.2.0 REQUIRED) -... -add_library(foo ...) -... -target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) -``` +!!! example -The package configuration file, `nlohmann_jsonConfig.cmake`, can be used either from an install tree or directly out of the build tree. + ```cmake title="CMakeLists.txt" + cmake_minimum_required(VERSION 3.1) + project(ExampleProject LANGUAGES CXX) + + find_package(nlohmann_json 3.10.5 REQUIRED) + + add_executable(example example.cpp) + target_link_libraries(example PRIVATE nlohmann_json::nlohmann_json) + ``` + +The package configuration file, `nlohmann_jsonConfig.cmake`, can be used either from an install tree or directly out of +the build tree. ### Embedded -To embed the library directly into an existing CMake project, place the entire source tree in a subdirectory and call `add_subdirectory()` in your `CMakeLists.txt` file: +To embed the library directly into an existing CMake project, place the entire source tree in a subdirectory and call +`add_subdirectory()` in your `CMakeLists.txt` file. -```cmake -# If you only include this third party in PRIVATE source files, you do not -# need to install it when your main project gets installed. -# set(JSON_Install OFF CACHE INTERNAL "") +!!! example -# Don't use include(nlohmann_json/CMakeLists.txt) since that carries with it -# unintended consequences that will break the build. It's generally -# discouraged (although not necessarily well documented as such) to use -# include(...) for pulling in other CMake projects anyways. -add_subdirectory(nlohmann_json) -... -add_library(foo ...) -... -target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) -``` + ```cmake title="CMakeLists.txt" + cmake_minimum_required(VERSION 3.1) + project(ExampleProject LANGUAGES CXX) -### Embedded (FetchContent) + # If you only include this third party in PRIVATE source files, you do not need to install it + # when your main project gets installed. + set(JSON_Install OFF CACHE INTERNAL "") + + add_subdirectory(nlohmann_json) -Since CMake v3.11, -[FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can -be used to automatically download the repository as a dependency at configure type. + add_executable(example example.cpp) + target_link_libraries(example PRIVATE nlohmann_json::nlohmann_json) + ``` -Example: -```cmake -include(FetchContent) +!!! note -FetchContent_Declare(json - GIT_REPOSITORY https://github.com/nlohmann/json - GIT_TAG v3.7.3) + Do not use `#!cmake include(nlohmann_json/CMakeLists.txt)`, since that carries with it unintended consequences that + will break the build. It is generally discouraged (although not necessarily well documented as such) to use + `#!cmake include(...)` for pulling in other CMake projects anyways. -FetchContent_GetProperties(json) -if(NOT json_POPULATED) - FetchContent_Populate(json) - add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) -endif() - -target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) -``` - -!!! Note - The repository download size is quite large. - You might want to depend on a smaller repository. For instance, you might want to replace the URL above by - . ### Supporting Both -To allow your project to support either an externally supplied or an embedded JSON library, you can use a pattern akin to the following: +To allow your project to support either an externally supplied or an embedded JSON library, you can use a pattern akin +to the following. -``` cmake -# Top level CMakeLists.txt -project(FOO) -... -option(FOO_USE_EXTERNAL_JSON "Use an external JSON library" OFF) -... -add_subdirectory(thirdparty) -... -add_library(foo ...) -... -# Note that the namespaced target will always be available regardless of the -# import method -target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) -``` -```cmake -# thirdparty/CMakeLists.txt -... -if(FOO_USE_EXTERNAL_JSON) - find_package(nlohmann_json 3.2.0 REQUIRED) -else() - set(JSON_BuildTests OFF CACHE INTERNAL "") - add_subdirectory(nlohmann_json) -endif() -... -``` +!!! example -`thirdparty/nlohmann_json` is then a complete copy of this source tree. + ```cmake title="CMakeLists.txt" + project(ExampleProject LANGUAGES CXX) + + option(EXAMPLE_USE_EXTERNAL_JSON "Use an external JSON library" OFF) + + add_subdirectory(thirdparty) + + add_executable(example example.cpp) + + # Note that the namespaced target will always be available regardless of the import method + target_link_libraries(example PRIVATE nlohmann_json::nlohmann_json) + ``` + + ```cmake title="thirdparty/CMakeLists.txt" + if(EXAMPLE_USE_EXTERNAL_JSON) + find_package(nlohmann_json 3.10.5 REQUIRED) + else() + set(JSON_BuildTests OFF CACHE INTERNAL "") + add_subdirectory(nlohmann_json) + endif() + ``` + + `thirdparty/nlohmann_json` is then a complete copy of this source tree. + + +### FetchContent + +Since CMake v3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can be used to +automatically download the repository as a dependency at configure type. + +!!! example + + ```cmake title="CMakeLists.txt" + cmake_minimum_required(VERSION 3.11) + project(ExampleProject LANGUAGES CXX) + + include(FetchContent) + + FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.10.5 + ) + + FetchContent_GetProperties(json) + if(NOT json_POPULATED) + FetchContent_Populate(json) + add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + add_executable(example example.cpp) + target_link_libraries(example PRIVATE nlohmann_json::nlohmann_json) + ``` + +!!! Note + + The repository download size is quite large. You might want to depend on a + smaller repository. For instance, you might want to replace the URL in the example by + . ## CMake Options diff --git a/doc/mkdocs/docs/integration/example.cpp b/doc/mkdocs/docs/integration/example.cpp index e5a31be4b..1a7ac4de2 100644 --- a/doc/mkdocs/docs/integration/example.cpp +++ b/doc/mkdocs/docs/integration/example.cpp @@ -1,9 +1,10 @@ #include #include +#include using json = nlohmann::json; int main() { - std::cout << json::meta() << std::endl; + std::cout << std::setw(4) << json::meta() << std::endl; } diff --git a/doc/mkdocs/docs/integration/index.md b/doc/mkdocs/docs/integration/index.md index 5ee4ff721..bfa94ae8a 100644 --- a/doc/mkdocs/docs/integration/index.md +++ b/doc/mkdocs/docs/integration/index.md @@ -1,6 +1,7 @@ # Header only -[`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is the single required file in `single_include/nlohmann` or [released here](https://github.com/nlohmann/json/releases). You need to add +[`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is the single required +file in `single_include/nlohmann` or [released here](https://github.com/nlohmann/json/releases). You need to add ```cpp #include @@ -9,6 +10,9 @@ using json = nlohmann::json; ``` -to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang). +to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and +Clang). -You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) for forward-declarations. The installation of `json_fwd.hpp` (as part of CMake's install step), can be achieved by setting `-DJSON_MultipleHeaders=ON`. +You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) +for forward-declarations. The installation of `json_fwd.hpp` (as part of CMake's install step), can be achieved by +setting `-DJSON_MultipleHeaders=ON`. diff --git a/doc/mkdocs/docs/integration/package_managers.md b/doc/mkdocs/docs/integration/package_managers.md index d98005da9..c2fb1a7f4 100644 --- a/doc/mkdocs/docs/integration/package_managers.md +++ b/doc/mkdocs/docs/integration/package_managers.md @@ -6,6 +6,12 @@ Throughout this page, we will describe how to compile the example file `example. --8<-- "integration/example.cpp" ``` +When executed, this program should create output similar to + +```json +--8<-- "../../examples/meta.output" +``` + ## Homebrew If you are using OS X and [Homebrew](http://brew.sh), just type @@ -26,11 +32,9 @@ instead. See [nlohmann-json](https://formulae.brew.sh/formula/nlohmann-json) for 1. Create the following file: - === "example.cpp" - - ```cpp - --8<-- "integration/example.cpp" - ``` + ```cpp title="example.cpp" + --8<-- "integration/example.cpp" + ``` 2. Install the package @@ -50,6 +54,8 @@ instead. See [nlohmann-json](https://formulae.brew.sh/formula/nlohmann-json) for clang++ example.cpp -I/usr/local/Cellar/nlohmann-json/3.7.3/include -std=c++11 -o example ``` +:material-update: The [formula](https://formulae.brew.sh/formula/nlohmann-json) is updated automatically. + ## Meson If you are using the [Meson Build System](http://mesonbuild.com), add this source tree as a [meson subproject](https://mesonbuild.com/Subprojects.html#using-a-subproject). You may also use the `include.zip` published in this project's [Releases](https://github.com/nlohmann/json/releases) to reduce the size of the vendored source tree. Alternatively, you can get a wrap file by downloading it from [Meson WrapDB](https://wrapdb.mesonbuild.com/nlohmann_json), or simply use `meson wrap install nlohmann_json`. Please see the meson project for any issues regarding the packaging. @@ -64,24 +70,17 @@ If you are using [Conan](https://www.conan.io/) to manage your dependencies, mer 1. Create the following files: - === "Conanfile.txt" - - ```ini - --8<-- "integration/conan/Conanfile.txt" - ``` + ```ini title="Conanfile.txt" + --8<-- "integration/conan/Conanfile.txt" + ``` - === "CMakeLists.txt" - - ```cmake - --8<-- "integration/conan/CMakeLists.txt" - ``` - - === "example.cpp" - - ```cpp - --8<-- "integration/conan/example.cpp" - ``` + ```cmake title="CMakeLists.txt" + --8<-- "integration/conan/CMakeLists.txt" + ``` + ```cpp title="example.cpp" + --8<-- "integration/conan/example.cpp" + ``` 2. Build: @@ -93,6 +92,8 @@ If you are using [Conan](https://www.conan.io/) to manage your dependencies, mer cmake --build . ``` +:material-update: The [package](https://conan.io/center/nlohmann_json) is updated automatically. + ## Spack If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the [`nlohmann-json` package](https://spack.readthedocs.io/en/latest/package_list.html#nlohmann-json). Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging. @@ -113,17 +114,13 @@ If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project fo 1. Create the following files: - === "CMakeLists.txt" - - ```cmake - --8<-- "integration/vcpkg/CMakeLists.txt" - ``` - - === "example.cpp" - - ```cpp - --8<-- "integration/vcpkg/example.cpp" - ``` + ```cmake title="CMakeLists.txt" + --8<-- "integration/vcpkg/CMakeLists.txt" + ``` + + ```cpp title="example.cpp" + --8<-- "integration/vcpkg/example.cpp" + ``` 2. Install package: @@ -146,6 +143,8 @@ If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project fo If you are using [cget](http://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install nlohmann/json`. A specific version can be installed with `cget install nlohmann/json@v3.1.0`. Also, the multiple header version can be installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`). +:material-update: cget reads directly from the [GitHub repository](https://github.com/nlohmann/json) and is always up-to-date. + ## CocoaPods If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open). @@ -162,19 +161,27 @@ If you are using [conda](https://conda.io/), you can use the package [nlohmann_j If you are using [MSYS2](http://www.msys2.org/), you can use the [mingw-w64-nlohmann-json](https://packages.msys2.org/base/mingw-w64-nlohmann-json) package, just type `pacman -S mingw-w64-i686-nlohmann-json` or `pacman -S mingw-w64-x86_64-nlohmann-json` for installation. Please file issues [here](https://github.com/msys2/MINGW-packages/issues/new?title=%5Bnlohmann-json%5D) if you experience problems with the packages. +:material-update: The [package](https://packages.msys2.org/base/mingw-w64-nlohmann-json) is updated automatically. + ## MacPorts If you are using [MacPorts](https://ports.macports.org), execute `sudo port install nlohmann-json` to install the [nlohmann-json](https://ports.macports.org/port/nlohmann-json/) package. +:material-update: The [package](https://ports.macports.org/port/nlohmann-json/) is updated automatically. + ## build2 -If you are using [`build2`](https://build2.org), you can use the [`nlohmann-json`](https://cppget.org/nlohmann-json) package from the public repository http://cppget.org or directly from the [package's sources repository](https://github.com/build2-packaging/nlohmann-json). In your project's `manifest` file, just add `depends: nlohmann-json` (probably with some [version constraints](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-add-remove-deps)). If you are not familiar with using dependencies in `build2`, [please read this introduction](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml). +If you are using [`build2`](https://build2.org), you can use the [`nlohmann-json`](https://cppget.org/nlohmann-json) package from the public repository or directly from the [package's sources repository](https://github.com/build2-packaging/nlohmann-json). In your project's `manifest` file, just add `depends: nlohmann-json` (probably with some [version constraints](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-add-remove-deps)). If you are not familiar with using dependencies in `build2`, [please read this introduction](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml). Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if you experience problems with the packages. +:material-update: The [package](https://cppget.org/nlohmann-json) is updated automatically. + ## wsjcpp If you are using [`wsjcpp`](http://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. +:material-update: wsjcpp reads directly from the [GitHub repository](https://github.com/nlohmann/json) and is always up-to-date. + ## CPM.cmake If you are using [`CPM.cmake`](https://github.com/TheLartians/CPM.cmake), you can check this [`example`](https://github.com/TheLartians/CPM.cmake/tree/master/examples/json). After [adding CPM script](https://github.com/TheLartians/CPM.cmake#adding-cpm) to your project, implement the following snippet to your CMake: From c6740d7d58a209da8960c961ee07bfb0e841e44e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 11 Jan 2022 21:46:40 +0100 Subject: [PATCH 010/110] :memo: add documentation for default behavior for macros --- doc/mkdocs/docs/features/macros.md | 116 ++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 4 deletions(-) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index d324a1325..d02bd8083 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -4,16 +4,41 @@ Some aspects of the library can be configured by defining preprocessor macros be ## `JSON_ASSERT(x)` -The default value is `#!cpp assert(x)`. +This marco controls which code is executed for runtime assertions of the libraries. + +!!! info "Default behavior" + + The default value is [`#!cpp assert(x)`](https://en.cppreference.com/w/cpp/error/assert). + + ```cpp + #define JSON_ASSERT(x) assert(x) + ``` ## `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. +This macro overrides [`#!cpp catch`](https://en.cppreference.com/w/cpp/language/try_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. +!!! info "Default behavior" + + When exceptions are enabled, the default value is + [`#!cpp catch(exception)`](https://en.cppreference.com/w/cpp/language/try_catch). + + ```cpp + #define JSON_CATCH_USER(exception) catch(exception) + ``` + + When exceptions are switched off by the compiler, the default value is `#!cpp if (false)` to make the catch block + unreachable. + + ```cpp + #define JSON_CATCH_USER(exception) if (false) + ``` + ## `JSON_DIAGNOSTICS` This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable @@ -26,6 +51,12 @@ that enabling this macro increases the size of every JSON value by one pointer a The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. +!!! info "Default behavior" + + ```cpp + #define JSON_DIAGNOSTICS 0 + ``` + ## `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, `JSON_HAS_CPP_20` The library targets C++11, but also supports some features introduced in later C++ versions (e.g., `std::string_view` @@ -34,6 +65,11 @@ standard. By defining any of these symbols, the internal check is overridden and unconditionally assumed. This can be helpful for compilers that only implement parts of the standard and would be detected incorrectly. +!!! info "Default behavior" + + The default value is detected based on the preprocessor macros `#!cpp __cplusplus`, `#!cpp _HAS_CXX17`, or + `#!cpp _MSVC_LANG`. + ## `JSON_HAS_FILESYSTEM`, `JSON_HAS_EXPERIMENTAL_FILESYSTEM` When compiling with C++17, the library provides conversions from and to `std::filesystem::path`. As compiler support @@ -41,12 +77,29 @@ for filesystem is limited, the library tries to detect whether ``/`s or ``/`std::experimental::filesystem` (`JSON_HAS_EXPERIMENTAL_FILESYSTEM`) should be used. To override the built-in check, define `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERIMENTAL_FILESYSTEM` to `1`. +!!! info "Default behavior" + + The default value is detected based on the preprocessor macros `#!cpp __cpp_lib_filesystem`, + `#!cpp __cpp_lib_experimental_filesystem`, `#!cpp __has_include()`, or + `#!cpp __has_include()`. + +Note that older compilers or older versions of libstd++ also require the library `stdc++fs` to be linked to for +filesystem support. + ## `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()`. +!!! info "Default behavior" + + By default, the macro is not defined. + + ```cpp + #undef JSON_NOEXCEPTION + ``` + The same effect is achieved by setting the compiler flag `-fno-exceptions`. Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not @@ -58,23 +111,72 @@ When defined, headers ``, ``, ``, ``, and ` Date: Wed, 12 Jan 2022 21:43:22 +0100 Subject: [PATCH 011/110] Fix a typo (#3265) --- doc/mkdocs/docs/features/macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index d02bd8083..533a01e20 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -4,7 +4,7 @@ Some aspects of the library can be configured by defining preprocessor macros be ## `JSON_ASSERT(x)` -This marco controls which code is executed for runtime assertions of the libraries. +This macro controls which code is executed for runtime assertions of the libraries. !!! info "Default behavior" From b772649624c53177cbd9e89129a08609ed6d6f5a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 25 Jan 2022 19:53:02 +0100 Subject: [PATCH 012/110] Add maintainer targets to create source archive (#3289) * :hammer: add script to create xz archive --- Makefile | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 43db74d85..597c609cb 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,6 @@ # configuration ########################################################################## -# directory to recent compiler binaries -COMPILER_DIR=/usr/local/opt/llvm/bin - # find GNU sed to use `-i` parameter SED:=$(shell command -v gsed || which sed) @@ -62,6 +59,7 @@ run_benchmarks: cd cmake-build-benchmarks ; ninja cd cmake-build-benchmarks ; ./json_benchmarks + ########################################################################## # fuzzing ########################################################################## @@ -135,6 +133,7 @@ pvs_studio: cd cmake-build-pvs-studio ; plog-converter -a'GA:1,2;64:1;CS' -t fullhtml PVS-Studio.log -o pvs open cmake-build-pvs-studio/pvs/index.html + ########################################################################## # Code format and source amalgamation ########################################################################## @@ -203,20 +202,29 @@ ChangeLog.md: # Release files ########################################################################## -# Create the files for a release and add signatures and hashes. We use `-X` to make the resulting ZIP file -# reproducible, see . +# Create a tar.gz archive that contains sufficient files to be used as CMake project (e.g., using FetchContent). The +# archive is created according to the advices of . +json.tar.xz: + mkdir json + rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include -type f) json + gtar --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz + rm -fr json -release: +# We use `-X` to make the resulting ZIP file reproducible, see +# . +include.zip: + zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) meson.build LICENSE.MIT + +# Create the files for a release and add signatures and hashes. +release: include.zip json.tar.xz rm -fr release_files mkdir release_files - zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) meson.build LICENSE.MIT gpg --armor --detach-sig include.zip - mv include.zip include.zip.asc release_files gpg --armor --detach-sig $(AMALGAMATED_FILE) + gpg --armor --detach-sig json.tar.xz cp $(AMALGAMATED_FILE) release_files - mv $(AMALGAMATED_FILE).asc release_files - cd release_files ; shasum -a 256 json.hpp > hashes.txt - cd release_files ; shasum -a 256 include.zip >> hashes.txt + mv $(AMALGAMATED_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files + cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt ########################################################################## @@ -225,12 +233,12 @@ release: # clean up clean: - rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM test/*.dSYM oclint_report.html + rm -fr fuzz fuzz-testing *.dSYM test/*.dSYM rm -fr benchmarks/files/numbers/*.json - rm -fr cmake-3.1.0-Darwin64.tar.gz cmake-3.1.0-Darwin64 - rm -fr cmake-build-benchmarks cmake-build-pedantic fuzz-testing cmake-build-clang-analyze cmake-build-pvs-studio cmake-build-infer cmake_build + rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files $(MAKE) clean -Cdoc + ########################################################################## # Thirdparty code ########################################################################## From 4d4c2730365084f2a68fb7a01110ae66fad059a3 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Wed, 26 Jan 2022 09:57:23 +0100 Subject: [PATCH 013/110] .github/workflows/windows.yml: Add support for Visual Studio 2022 (#3295) * .github/workflows/windows.yml: Add support for Visual Studio 2022 This is available in github actions since some time now [1]. [1]: https://github.com/actions/virtual-environments/issues/3949 * README.md: Add VS 2022 [skip ci] The version and the build engine version (aka MSBuild version) were taken from [1]. [1]: https://github.com/actions/virtual-environments/blob/win22/20220116.1/images/win/Windows2022-Readme.md --- .github/workflows/windows.yml | 32 ++++++++++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 34 insertions(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 597da4774..9d0989a51 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -95,6 +95,38 @@ jobs: - name: test run: cd build ; ctest -j 10 -C Release --output-on-failure + msvc2022: + runs-on: windows-2022 + strategy: + matrix: + build_type: [Debug, Release] + architecture: [Win32, x64] + + steps: + - uses: actions/checkout@v2 + - name: cmake + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" + if: matrix.build_type == 'Release' + - name: cmake + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX" + if: matrix.build_type == 'Debug' + - name: build + run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 + - name: test + run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure + + msvc2022_latest: + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v2 + - name: cmake + run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" + - name: build + run: cmake --build build --config Release --parallel 10 + - name: test + run: cd build ; ctest -j 10 -C Release --output-on-failure + clang: runs-on: windows-latest strategy: diff --git a/README.md b/README.md index 313296323..a9a6b7d1b 100644 --- a/README.md +++ b/README.md @@ -1045,6 +1045,7 @@ Though it's 2022 already, the support for C++11 is still a bit sparse. Currently - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) - Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later) - Microsoft Visual C++ 2019 / Build Tools 16.3.1+1def00d3d (and possibly later) +- Microsoft Visual C++ 2022 / Build Tools 19.30.30709.0 (and possibly later) I would be happy to learn about other compilers/versions. @@ -1119,6 +1120,7 @@ The following compilers are currently used in continuous integration at [AppVeyo | Visual Studio 15 2017 MSVC 19.16.27045.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | GitHub Actions | | Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | GitHub Actions | | Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | AppVeyor | +| Visual Studio 17 2022 MSVC 19.30.30709.0 (Build Engine version 17.0.31804.368 for .NET Framework) | Windows-10.0.20348 | GitHub Actions | ## Integration From c11f98228dac03fdddc89e5991861fc306874ef9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 30 Jan 2022 13:05:18 +0100 Subject: [PATCH 014/110] :memo: document FetchContent --- README.md | 12 +++++------- doc/mkdocs/docs/integration/cmake.md | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a9a6b7d1b..b8a143c15 100644 --- a/README.md +++ b/README.md @@ -1185,15 +1185,15 @@ target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) Since CMake v3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can -be used to automatically download the repository as a dependency at configure time. +be used to automatically download a release as a dependency at configure time. Example: ```cmake include(FetchContent) FetchContent_Declare(json - GIT_REPOSITORY https://github.com/nlohmann/json.git - GIT_TAG v3.7.3) + URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz +) FetchContent_GetProperties(json) if(NOT json_POPULATED) @@ -1204,10 +1204,8 @@ endif() target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` -**Note**: The repository https://github.com/nlohmann/json download size is huge. -It contains all the dataset used for the benchmarks. You might want to depend on -a smaller repository. For instance, you might want to replace the URL above by -https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent +**Note**: It is recommended to use the URL approach described above which is supported as of version 3.10.0. See + for more information. #### Supporting Both diff --git a/doc/mkdocs/docs/integration/cmake.md b/doc/mkdocs/docs/integration/cmake.md index 44aeb26e1..dbb8c2d66 100644 --- a/doc/mkdocs/docs/integration/cmake.md +++ b/doc/mkdocs/docs/integration/cmake.md @@ -90,7 +90,7 @@ to the following. ### FetchContent Since CMake v3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can be used to -automatically download the repository as a dependency at configure type. +automatically download a release as a dependency at configure type. !!! example @@ -101,8 +101,7 @@ automatically download the repository as a dependency at configure type. include(FetchContent) FetchContent_Declare(json - GIT_REPOSITORY https://github.com/nlohmann/json - GIT_TAG v3.10.5 + URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz ) FetchContent_GetProperties(json) @@ -117,8 +116,18 @@ automatically download the repository as a dependency at configure type. !!! Note - The repository download size is quite large. You might want to depend on a - smaller repository. For instance, you might want to replace the URL in the example by + It is recommended to use the URL approach described above which is supported as of version 3.10.0. It is also + possible to pass the Git repository like + + ```cmake + FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.10.5 + ) + ``` + + However, the repository download size is quite large. You might want to depend on + a smaller repository. For instance, you might want to replace the URL in the example by . ## CMake Options From eec79d4e8a306d0dc389d6ab7294478249069e51 Mon Sep 17 00:00:00 2001 From: pketelsen Date: Sun, 30 Jan 2022 22:06:50 +0100 Subject: [PATCH 015/110] Add macros NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and ..._NON_INTRUSIVE_WITH_DEFAULT (#3143) * Added new macros NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT. * Updated docs for NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT accordingly * Rephrased docs for NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT * Updated docs for NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT * Renamed default_obj in to avoid name clashes * Added test for serialization of default constructed object * Add const to getters for macro tests Co-authored-by: Chaoya Li --- doc/mkdocs/docs/features/arbitrary_types.md | 20 ++-- doc/mkdocs/docs/features/macros.md | 12 ++- include/nlohmann/detail/macro_scope.hpp | 9 ++ single_include/nlohmann/json.hpp | 9 ++ test/src/unit-udt_macro.cpp | 107 +++++++++++++++++++- 5 files changed, 145 insertions(+), 12 deletions(-) diff --git a/doc/mkdocs/docs/features/arbitrary_types.md b/doc/mkdocs/docs/features/arbitrary_types.md index fe80dc701..67e8a4554 100644 --- a/doc/mkdocs/docs/features/arbitrary_types.md +++ b/doc/mkdocs/docs/features/arbitrary_types.md @@ -85,29 +85,31 @@ Some important things: 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: +There are four 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 the namespace of the class/struct to create code for. -- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. +- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for. It will throw an exception in `from_json()` due to a missing value in the JSON object. +- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. +- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. It will throw an exception in `from_json()` due to a missing value in the JSON object. +- `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. -In both macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. +In all macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. You can read more docs about them starting from [here](macros.md#nlohmann_define_type_intrusivetype-member). !!! note - At most 64 member variables can be passed to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` or `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. + At most 64 member variables can be passed to these macros. ??? 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 { @@ -115,7 +117,7 @@ In both macros, the first parameter is the name of the class/struct, and all rem std::string street; int housenumber; int postcode; - + public: NLOHMANN_DEFINE_TYPE_INTRUSIVE(address, street, housenumber, postcode) }; diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index 533a01e20..3d2203e72 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -186,12 +186,12 @@ When defined to `0`, implicit conversions are switched off. By default, implicit ??? example This is an example for an implicit conversion: - + ```cpp json j = "Hello, world!"; std::string s = j; ``` - + When `JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`, the code above does no longer compile. Instead, it must be written like this: @@ -220,6 +220,10 @@ The first parameter is the name of the class/struct, and all remaining parameter See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example. +## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)` + +This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the type be default constructible. The `from_json()` function default constructs an object and uses its values as the defaults when calling the `value()` function. + ## `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 @@ -231,6 +235,10 @@ first parameter is the name of the class/struct, and all remaining parameters na See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example. +## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)` + +This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the type be default constructible. The `from_json()` function default constructs an object and uses its values as the defaults when calling the `value()` function. + ## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` This macro simplifies the serialization/deserialization of enum types. See diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index bcb70e911..87f964353 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -334,6 +334,7 @@ #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); /*! @brief macro @@ -344,6 +345,10 @@ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @@ -353,6 +358,10 @@ inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 92308c557..e40e3b05b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2631,6 +2631,7 @@ using is_detected_convertible = #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); /*! @brief macro @@ -2641,6 +2642,10 @@ using is_detected_convertible = friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @@ -2650,6 +2655,10 @@ using is_detected_convertible = inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index 5aef22797..1762d08da 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -59,6 +59,42 @@ class person_with_private_data NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata) }; +class person_with_private_data_2 +{ + private: + std::string name{}; + int age = 0; + json metadata = nullptr; + + public: + bool operator==(const person_with_private_data_2& rhs) const + { + return name == rhs.name && age == rhs.age && metadata == rhs.metadata; + } + + person_with_private_data_2() = default; + person_with_private_data_2(std::string name_, int age_, json metadata_) + : name(std::move(name_)) + , age(age_) + , metadata(std::move(metadata_)) + {} + + std::string getName() const + { + return name; + } + int getAge() const + { + return age; + } + json getMetadata() const + { + return metadata; + } + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person_with_private_data_2, age, name, metadata) +}; + class person_without_private_data_1 { public: @@ -103,6 +139,41 @@ class person_without_private_data_2 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata) +class person_without_private_data_3 +{ + public: + std::string name{}; + int age = 0; + json metadata = nullptr; + + bool operator==(const person_without_private_data_3& rhs) const + { + return name == rhs.name && age == rhs.age && metadata == rhs.metadata; + } + + person_without_private_data_3() = default; + person_without_private_data_3(std::string name_, int age_, json metadata_) + : name(std::move(name_)) + , age(age_) + , metadata(std::move(metadata_)) + {} + + std::string getName() const + { + return name; + } + int getAge() const + { + return age; + } + json getMetadata() const + { + return metadata; + } +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata) + class person_with_private_alphabet { public: @@ -231,7 +302,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f } // namespace persons -TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE", T, +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, persons::person_with_private_data, persons::person_without_private_data_1, persons::person_without_private_data_2) @@ -257,6 +328,40 @@ TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRU } } +TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T, + persons::person_with_private_data_2, + persons::person_without_private_data_3) +{ + SECTION("person with default values") + { + // serialization of default constructed object + T p0; + CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}"); + + // serialization + T p1("Erik", 1, {{"haircuts", 2}}); + CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); + + // deserialization + auto p2 = json(p1).get(); + CHECK(p2 == p1); + + // roundtrip + CHECK(T(json(p1)) == p1); + CHECK(json(T(json(p1))) == json(p1)); + + // check default value in case of missing field + json j = json(p1); + j.erase("name"); + j.erase("age"); + j.erase("metadata"); + T p3 = j.get(); + CHECK(p3.getName() == ""); + CHECK(p3.getAge() == 0); + CHECK(p3.getMetadata() == nullptr); + } +} + TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, persons::person_with_private_alphabet, persons::person_with_public_alphabet) From eb2182414749825be086c825edb5229e5c28503d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 8 Feb 2022 22:12:49 +0100 Subject: [PATCH 016/110] :memo: replace Doxygen links --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b8a143c15..89676cf37 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/nlohmann/json.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nlohmann/json/context:cpp) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/json.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/1mp10JbaANo6FUc7) -[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](https://nlohmann.github.io/json/doxygen/index.html) +[![Documentation](https://img.shields.io/badge/docs-mkdocs-blue.svg)](https://json.nlohmann.me) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![GitHub Downloads](https://img.shields.io/github/downloads/nlohmann/json/total)](https://github.com/nlohmann/json/releases) @@ -85,7 +85,7 @@ Thanks everyone! :books: If you want to **learn more** about how to use the library, check out the rest of the [**README**](#examples), have a look at [**code examples**](https://github.com/nlohmann/json/tree/develop/doc/examples), or browse through the [**help pages**](https://json.nlohmann.me). -:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or the [**Doxygen documentation**](https://json.nlohmann.me/doxygen/index.html). +:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/). :bug: If you found a **bug**, please check the [**FAQ**](https://json.nlohmann.me/home/faq/) if it is a known issue or the result of a design decision. Please also have a look at the [**issue list**](https://github.com/nlohmann/json/issues) before you [**create a new issue**](https://github.com/nlohmann/json/issues/new/choose). Please provide as much information as possible to help us understand and reproduce your issue. @@ -1624,13 +1624,14 @@ The library itself consists of a single header file licensed under the MIT licen - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json) - [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis - [**doctest**](https://github.com/onqtam/doctest) for the unit tests -- [**Doxygen**](https://www.doxygen.nl/index.html) to generate [documentation](https://nlohmann.github.io/json/doxygen/index.html) - [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages - [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md) - [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks - [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros - [**lcov**](http://ltp.sourceforge.net/coverage/lcov.php) to process coverage information and create an HTML view - [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz +- [**Material for MkDocs**](https://squidfunk.github.io/mkdocs-material/) for the style of the documentation site +- [**MkDocs**](https://www.mkdocs.org) for the documentation site - [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json)) - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. - [**Valgrind**](https://valgrind.org) to check for correct memory management From d8a63291cbe50411a2c513d06f3ae7c8c1a43c33 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 12 Feb 2022 15:45:51 +0100 Subject: [PATCH 017/110] :memo: add note on parsing ordered_json #3325 (#3326) --- doc/mkdocs/docs/features/object_order.md | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/doc/mkdocs/docs/features/object_order.md b/doc/mkdocs/docs/features/object_order.md index 86bb253ba..0768f8020 100644 --- a/doc/mkdocs/docs/features/object_order.md +++ b/doc/mkdocs/docs/features/object_order.md @@ -2,6 +2,8 @@ The [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". As such, an implementation does not need to preserve any specific order of object keys. +## Default behavior: sort keys + The default type `nlohmann::json` uses a `std::map` to store JSON objects, and thus stores object keys **sorted alphabetically**. ??? example @@ -33,6 +35,8 @@ The default type `nlohmann::json` uses a `std::map` to store JSON objects, and t } ``` +## Alternative behavior: preserve insertion order + If you do want to preserve the **insertion order**, you can try the type [`nlohmann::ordered_json`](https://github.com/nlohmann/json/issues/2179). ??? example @@ -65,3 +69,58 @@ If you do want to preserve the **insertion order**, you can try the type [`nlohm ``` Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)). + +### Notes on parsing + +Note that you also need to call the right [`parse`](../api/basic_json/parse.md) function when reading from a file. +Assume file `input.json` contains the JSON object above: + +```json +{ + "one": 1, + "two": 2, + "three": 3 +} +``` + +!!! success "Right way" + + The following code correctly calls the `parse` function from `nlohmann::ordered_json`: + + ```cpp + std::ifstream i("input.json"); + auto j = nlohmann::ordered_json::parse(i); + std::cout << j.dump(2) << std::endl; + ``` + + The output will be: + + ```json + { + "one": 1, + "two": 2, + "three": 3 + } + ``` + +??? failure "Wrong way" + + The following code incorrectly calls the `parse` function from `nlohmann::json` which does not preserve the + insertion order, but sorts object keys. Assigning the result to `nlohmann::ordered_json` compiles, but does not + restore the order from the input file. + + ```cpp + std::ifstream i("input.json"); + nlohmann::ordered_json j = nlohmann::json::parse(i); + std::cout << j.dump(2) << std::endl; + ``` + + The output will be: + + ```json + { + "one": 1, + "three": 3 + "two": 2, + } + ``` From e4643d1f1b03fc7a1d7b65f17e012ca93680cad8 Mon Sep 17 00:00:00 2001 From: Faruk D <144492+fdiblen@users.noreply.github.com> Date: Fri, 18 Feb 2022 18:11:44 +0100 Subject: [PATCH 018/110] Fix CITATION.cff and add automatic validation of your citation metadata (#3320) * Update CITATION.cff cffversion to 1.2.0 * Fix CITATION.cff date-released Co-authored-by: Abel Soares Siqueira --- CITATION.cff | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 7c28d04fd..40a1d26a2 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,6 +1,6 @@ -cff-version: 1.1.0 +cff-version: 1.2.0 message: "If you use this software, please cite it as below." -authors: +authors: - family-names: Lohmann given-names: Niels orcid: https://orcid.org/0000-0001-9037-795X @@ -8,7 +8,7 @@ authors: website: https://nlohmann.me title: "JSON for Modern C++" version: 3.10.5 -date-released: 2022 +date-released: 2022-01-03 license: MIT repository-code: "https://github.com/nlohmann" url: https://json.nlohmann.me From d1e57df48ba72083697d45720a3a0bec227e0f5e Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sun, 6 Mar 2022 13:54:00 +0100 Subject: [PATCH 019/110] Fix and update CI (#3368) * CI: add workflow_dispatch trigger * CI: change msvc2019*/clang* runners to windows-2019 GitHub updated their runners. windows-latest is now based on Windows Server 2022 and comes with different tool versions. MSVC 2019 is still available via the windows-2019 runner. --- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/macos.yml | 1 + .github/workflows/ubuntu.yml | 1 + .github/workflows/windows.yml | 9 +++++---- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 20275feac..01b333497 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,6 +9,7 @@ on: pull_request: schedule: - cron: '0 19 * * 1' + workflow_dispatch: jobs: CodeQL-Build: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f653e2b6b..6a378f13f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -7,6 +7,7 @@ on: - master - release/* pull_request: + workflow_dispatch: jobs: xcode: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index eb544007e..634545a4f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -7,6 +7,7 @@ on: - master - release/* pull_request: + workflow_dispatch: jobs: ci_test_clang: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9d0989a51..e8ca95d51 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -7,6 +7,7 @@ on: - master - release/* pull_request: + workflow_dispatch: jobs: mingw: @@ -64,7 +65,7 @@ jobs: run: cd build ; ctest -j 10 -C Release --output-on-failure msvc2019: - runs-on: windows-latest + runs-on: windows-2019 strategy: matrix: build_type: [Debug, Release] @@ -84,7 +85,7 @@ jobs: run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure msvc2019_latest: - runs-on: windows-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v2 @@ -128,7 +129,7 @@ jobs: run: cd build ; ctest -j 10 -C Release --output-on-failure clang: - runs-on: windows-latest + runs-on: windows-2019 strategy: matrix: version: [11, 12] @@ -145,7 +146,7 @@ jobs: run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure clang-cl-11: - runs-on: windows-latest + runs-on: windows-2019 strategy: matrix: architecture: [Win32, x64] From c6d8892e5d1bac2803b53063fe3e3c23efe46574 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 6 Mar 2022 15:33:05 +0100 Subject: [PATCH 020/110] FetchContent_MakeAvailable (#3351) * :wrench: use FetchContent_MakeAvailable * :green_heart: fix test names --- README.md | 11 ++-------- doc/mkdocs/docs/integration/cmake.md | 11 ++-------- test/CMakeLists.txt | 1 + test/cmake_fetch_content2/CMakeLists.txt | 20 +++++++++++++++++++ .../project/CMakeLists.txt | 15 ++++++++++++++ test/cmake_fetch_content2/project/main.cpp | 8 ++++++++ 6 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 test/cmake_fetch_content2/CMakeLists.txt create mode 100644 test/cmake_fetch_content2/project/CMakeLists.txt create mode 100644 test/cmake_fetch_content2/project/main.cpp diff --git a/README.md b/README.md index 89676cf37..c6babb64e 100644 --- a/README.md +++ b/README.md @@ -1191,15 +1191,8 @@ Example: ```cmake include(FetchContent) -FetchContent_Declare(json - URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz -) - -FetchContent_GetProperties(json) -if(NOT json_POPULATED) - FetchContent_Populate(json) - add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) -endif() +FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz) +FetchContent_MakeAvailable(json) target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) ``` diff --git a/doc/mkdocs/docs/integration/cmake.md b/doc/mkdocs/docs/integration/cmake.md index dbb8c2d66..2247ba009 100644 --- a/doc/mkdocs/docs/integration/cmake.md +++ b/doc/mkdocs/docs/integration/cmake.md @@ -100,15 +100,8 @@ automatically download a release as a dependency at configure type. include(FetchContent) - FetchContent_Declare(json - URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz - ) - - FetchContent_GetProperties(json) - if(NOT json_POPULATED) - FetchContent_Populate(json) - add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) - endif() + FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz) + FetchContent_MakeAvailable(json) add_executable(example example.cpp) target_link_libraries(example PRIVATE nlohmann_json::nlohmann_json) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4c741f274..c29662134 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -170,4 +170,5 @@ endif() add_subdirectory(cmake_add_subdirectory) add_subdirectory(cmake_fetch_content) +add_subdirectory(cmake_fetch_content2) add_subdirectory(cmake_target_include_directories) diff --git a/test/cmake_fetch_content2/CMakeLists.txt b/test/cmake_fetch_content2/CMakeLists.txt new file mode 100644 index 000000000..40e75e832 --- /dev/null +++ b/test/cmake_fetch_content2/CMakeLists.txt @@ -0,0 +1,20 @@ +if (${CMAKE_VERSION} VERSION_GREATER "3.14.0") + add_test(NAME cmake_fetch_content2_configure + COMMAND ${CMAKE_COMMAND} + -G "${CMAKE_GENERATOR}" + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -Dnlohmann_json_source=${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/project + ) + add_test(NAME cmake_fetch_content2_build + COMMAND ${CMAKE_COMMAND} --build . + ) + set_tests_properties(cmake_fetch_content2_configure PROPERTIES + FIXTURES_SETUP cmake_fetch_content2 + LABELS "git_required;not_reproducible" + ) + set_tests_properties(cmake_fetch_content2_build PROPERTIES + FIXTURES_REQUIRED cmake_fetch_content2 + LABELS "git_required;not_reproducible" + ) +endif() diff --git a/test/cmake_fetch_content2/project/CMakeLists.txt b/test/cmake_fetch_content2/project/CMakeLists.txt new file mode 100644 index 000000000..734b9cc0e --- /dev/null +++ b/test/cmake_fetch_content2/project/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.14) + +project(DummyImport CXX) + +include(FetchContent) + +get_filename_component(GIT_REPOSITORY_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../.. ABSOLUTE) +FetchContent_Declare(json GIT_REPOSITORY ${GIT_REPOSITORY_DIRECTORY} GIT_TAG HEAD) +FetchContent_MakeAvailable(json) + +add_executable(with_namespace_target main.cpp) +target_link_libraries(with_namespace_target nlohmann_json::nlohmann_json) + +add_executable(without_namespace_target main.cpp) +target_link_libraries(without_namespace_target nlohmann_json) diff --git a/test/cmake_fetch_content2/project/main.cpp b/test/cmake_fetch_content2/project/main.cpp new file mode 100644 index 000000000..d2d118b85 --- /dev/null +++ b/test/cmake_fetch_content2/project/main.cpp @@ -0,0 +1,8 @@ +#include + +int main(int argc, char **argv) +{ + nlohmann::json j; + + return 0; +} From 0fd95d2e28dd739434ab3733d5bf86b2ff942035 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Mon, 7 Mar 2022 13:41:35 +0100 Subject: [PATCH 021/110] Fix ordered_map ctor with initializer_list (#3370) One of the ordered_map constructors was incorrectly accepting a std::initializer_list instead of std::initializer_list. Add regression test. Fixes #3343. --- include/nlohmann/ordered_map.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- test/src/unit-regression2.cpp | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index bfcf89a40..709bc5fd5 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -34,7 +34,7 @@ template , template ordered_map(It first, It last, const Allocator& alloc = Allocator()) : Container{first, last, alloc} {} - ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) : Container{init, alloc} {} std::pair emplace(const key_type& key, T&& t) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e40e3b05b..ec33a847a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17070,7 +17070,7 @@ template , template ordered_map(It first, It last, const Allocator& alloc = Allocator()) : Container{first, last, alloc} {} - ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) : Container{init, alloc} {} std::pair emplace(const key_type& key, T&& t) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 52db3589c..6249db63e 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -835,6 +835,18 @@ TEST_CASE("regression tests 2") CHECK(j.dump() == "[1,4]"); } + + SECTION("issue #3343 - json and ordered_json are not interchangable") + { + json::object_t jobj({ { "product", "one" } }); + ordered_json::object_t ojobj({{"product", "one"}}); + + auto jit = jobj.begin(); + auto ojit = ojobj.begin(); + + CHECK(jit->first == ojit->first); + CHECK(jit->second.get() == ojit->second.get()); + } } DOCTEST_CLANG_SUPPRESS_WARNING_POP From 8d7b5b6a287b08af3a495c36ad97ce8a4a899930 Mon Sep 17 00:00:00 2001 From: Andrea Cocito <39852324+puffetto@users.noreply.github.com> Date: Mon, 7 Mar 2022 13:43:50 +0100 Subject: [PATCH 022/110] Add clarification to avoid misunderstanding that cause #3360 (#3378) * Update macros.md Typos, typos --- doc/mkdocs/docs/features/macros.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index 3d2203e72..cade9d5bc 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -51,6 +51,8 @@ that enabling this macro increases the size of every JSON value by one pointer a The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. +WARNING: As this macro changes the definition of the json object, it MUST be defined in the same way globally, even across different compilation units; do NOT link together code compiled with and without JSON_DIAGNOSTICS defined as this is a violation of the One Definition Rule and will cause undefined behaviour. + !!! info "Default behavior" ```cpp From 4a6e6ca8c7a63802fb15d95ee8aafff310e3a6de Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 7 Mar 2022 13:48:24 +0100 Subject: [PATCH 023/110] :memo: update documentation --- doc/mkdocs/docs/features/macros.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index cade9d5bc..9994c5dcd 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -51,7 +51,11 @@ that enabling this macro increases the size of every JSON value by one pointer a The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. -WARNING: As this macro changes the definition of the json object, it MUST be defined in the same way globally, even across different compilation units; do NOT link together code compiled with and without JSON_DIAGNOSTICS defined as this is a violation of the One Definition Rule and will cause undefined behaviour. +!!! warning + + As this macro changes the definition of the `basic_json` object, it MUST be defined in the same way globally, even + across different compilation units; DO NOT link together code compiled with different definitions of + `JSON_DIAGNOSTICS` as this is a violation of the One Definition Rule and will cause undefined behaviour. !!! info "Default behavior" From f208a9c19b6c158826aa00b6d0642be29104c18a Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Mon, 7 Mar 2022 22:19:28 +0100 Subject: [PATCH 024/110] Fix C++20/gcc-12 issues (Part 1) (#3379) * :wrench: use proper GCC binary * :wrench: add more GCC warning flags * :alembic: try fix from https://github.com/nlohmann/json/issues/3138#issuecomment-1015562666 * Fix custom allocator test build failures (C++20) Allocator tests fail to compile in C++20 mode with clang+MS STL due to missing copy constructors. * Fix test build failures due to missing noexcept (gcc-12) * alt_string has multiple member functions that should be marked noexcept. * nlohmann::ordered_map constructors can be noexcept. Compilation failures result from the warning flag -Werror=noexcept and gcc-12. * Disable broken comparison tests in C++20 mode Co-authored-by: Niels Lohmann --- cmake/ci.cmake | 27 ++++++++++++++++++++------- include/nlohmann/json.hpp | 1 + include/nlohmann/ordered_map.hpp | 3 ++- single_include/nlohmann/json.hpp | 4 +++- test/src/unit-allocator.cpp | 2 ++ test/src/unit-alt-string.cpp | 4 ++-- test/src/unit-comparison.cpp | 12 ++++++++++++ test/src/unit-regression2.cpp | 7 +++++-- 8 files changed, 47 insertions(+), 13 deletions(-) diff --git a/cmake/ci.cmake b/cmake/ci.cmake index d0b989c80..9a8136165 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -30,7 +30,7 @@ execute_process(COMMAND ${CPPCHECK_TOOL} --version OUTPUT_VARIABLE CPPCHECK_TOOL string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CPPCHECK_TOOL_VERSION "${CPPCHECK_TOOL_VERSION}") message(STATUS "🔖 Cppcheck ${CPPCHECK_TOOL_VERSION} (${CPPCHECK_TOOL})") -find_program(GCC_TOOL NAMES g++-HEAD g++-11 g++-latest) +find_program(GCC_TOOL NAMES g++-latest g++-HEAD g++-11) execute_process(COMMAND ${GCC_TOOL} --version OUTPUT_VARIABLE GCC_TOOL_VERSION ERROR_VARIABLE GCC_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" GCC_TOOL_VERSION "${GCC_TOOL_VERSION}") message(STATUS "🔖 GCC ${GCC_TOOL_VERSION} (${GCC_TOOL})") @@ -111,6 +111,7 @@ set(CLANG_CXXFLAGS "-std=c++11 \ -Wno-reserved-identifier \ ") +# Warning flags determined for GCC 12.0 (experimental) with https://github.com/nlohmann/gcc_flags: # Ignored GCC warnings: # -Wno-abi-tag We do not care about ABI tags. # -Wno-aggregate-return The library uses aggregate returns. @@ -150,16 +151,22 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wanalyzer-shift-count-negative \ -Wanalyzer-shift-count-overflow \ -Wanalyzer-stale-setjmp-buffer \ + -Wanalyzer-tainted-allocation-size \ -Wanalyzer-tainted-array-index \ + -Wanalyzer-tainted-divisor \ + -Wanalyzer-tainted-offset \ + -Wanalyzer-tainted-size \ -Wanalyzer-too-complex \ -Wanalyzer-unsafe-call-within-signal-handler \ -Wanalyzer-use-after-free \ -Wanalyzer-use-of-pointer-in-stale-stack-frame \ + -Wanalyzer-use-of-uninitialized-value \ -Wanalyzer-write-to-const \ -Wanalyzer-write-to-string-literal \ -Warith-conversion \ -Warray-bounds \ -Warray-bounds=2 \ + -Warray-compare \ -Warray-parameter=2 \ -Wattribute-alias=2 \ -Wattribute-warning \ @@ -170,10 +177,15 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wbuiltin-macro-redefined \ -Wc++0x-compat \ -Wc++11-compat \ + -Wc++11-extensions \ -Wc++14-compat \ + -Wc++14-extensions \ -Wc++17-compat \ + -Wc++17-extensions \ -Wc++1z-compat \ -Wc++20-compat \ + -Wc++20-extensions \ + -Wc++23-extensions \ -Wc++2a-compat \ -Wcannot-profile \ -Wcast-align \ @@ -191,6 +203,7 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wconditionally-supported \ -Wconversion \ -Wconversion-null \ + -Wcoverage-invalid-line-number \ -Wcoverage-mismatch \ -Wcpp \ -Wctad-maybe-unsupported \ @@ -215,21 +228,16 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wendif-labels \ -Wenum-compare \ -Wenum-conversion \ + -Wexceptions \ -Wexpansion-to-defined \ -Wextra \ -Wextra-semi \ -Wfloat-conversion \ -Wfloat-equal \ - -Wformat-contains-nul \ -Wformat-diag \ - -Wformat-extra-args \ - -Wformat-nonliteral \ -Wformat-overflow=2 \ - -Wformat-security \ -Wformat-signedness \ -Wformat-truncation=2 \ - -Wformat-y2k \ - -Wformat-zero-length \ -Wformat=2 \ -Wframe-address \ -Wfree-nonheap-object \ @@ -239,12 +247,15 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wignored-qualifiers \ -Wimplicit-fallthrough=5 \ -Winaccessible-base \ + -Winfinite-recursion \ -Winherited-variadic-ctor \ -Winit-list-lifetime \ -Winit-self \ -Winline \ -Wint-in-bool-context \ -Wint-to-pointer-cast \ + -Winterference-size \ + -Winvalid-imported-macros \ -Winvalid-memory-model \ -Winvalid-offsetof \ -Winvalid-pch \ @@ -267,6 +278,7 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wmissing-field-initializers \ -Wmissing-include-dirs \ -Wmissing-profile \ + -Wmissing-requires \ -Wmultichar \ -Wmultiple-inheritance \ -Wmultistatement-macros \ @@ -282,6 +294,7 @@ set(GCC_CXXFLAGS "-std=c++11 \ -Wnull-dereference \ -Wodr \ -Wold-style-cast \ + -Wopenacc-parallelism \ -Wopenmp-simd \ -Woverflow \ -Woverlength-strings \ diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index a79be2dbd..6bf77d507 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1872,6 +1872,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < typename ValueType, typename std::enable_if < detail::conjunction < detail::negation>, + detail::negation>, detail::negation>>, detail::negation>, detail::negation>, diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 709bc5fd5..5a7da074b 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -30,7 +30,8 @@ template , // Explicit constructors instead of `using Container::Container` // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) - ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + ordered_map() noexcept(noexcept(Container())) : Container{} {} + explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {} template ordered_map(It first, It last, const Allocator& alloc = Allocator()) : Container{first, last, alloc} {} diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ec33a847a..48999920d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17066,7 +17066,8 @@ template , // Explicit constructors instead of `using Container::Container` // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) - ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + ordered_map() noexcept(noexcept(Container())) : Container{} {} + explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {} template ordered_map(It first, It last, const Allocator& alloc = Allocator()) : Container{first, last, alloc} {} @@ -19052,6 +19053,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < typename ValueType, typename std::enable_if < detail::conjunction < detail::negation>, + detail::negation>, detail::negation>>, detail::negation>, detail::negation>, diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 2671212f5..268ae54df 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -39,6 +39,8 @@ namespace template struct bad_allocator : std::allocator { + using std::allocator::allocator; + template void construct(T* /*unused*/, Args&& ... /*unused*/) { diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp index 2c599a451..7da69dea1 100644 --- a/test/src/unit-alt-string.cpp +++ b/test/src/unit-alt-string.cpp @@ -104,12 +104,12 @@ class alt_string } template - bool operator<(const op_type& op) const + bool operator<(const op_type& op) const noexcept { return str_impl < op; } - bool operator<(const alt_string& op) const + bool operator<(const alt_string& op) const noexcept { return str_impl < op.str_impl; } diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 4feccef44..f4853f585 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -32,6 +32,10 @@ SOFTWARE. #include using nlohmann::json; +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 +#endif + namespace { // helper function to check std::less @@ -205,6 +209,14 @@ TEST_CASE("lexicographical comparison operators") { for (size_t j = 0; j < j_values.size(); ++j) { + // Skip comparing indicies 12 and 13, and 13 and 12 in C++20 pending fix + // See issue #3207 +#ifdef JSON_HAS_CPP_20 + if ((i == 12 && j == 13) || (i == 13 && j == 12)) + { + continue; + } +#endif CAPTURE(i) CAPTURE(j) CAPTURE(j_values[i]) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 6249db63e..8c6e41193 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -271,7 +271,10 @@ std::string* sax_no_exception::error_string = nullptr; template class my_allocator : public std::allocator -{}; +{ + public: + using std::allocator::allocator; +}; ///////////////////////////////////////////////////////////////////// // for #3077 @@ -338,7 +341,7 @@ TEST_CASE("regression tests 2") ] })"; - json::parser_callback_t cb = [&](int /*level*/, json::parse_event_t event, json & parsed) + json::parser_callback_t cb = [&](int /*level*/, json::parse_event_t event, json & parsed) noexcept { // skip uninteresting events if (event == json::parse_event_t::value && !parsed.is_primitive()) From 700b95f4473cac1d900b807541daad0ca3d98f52 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Tue, 8 Mar 2022 10:10:50 +0100 Subject: [PATCH 025/110] Make iterator operator++/--(int) equality-preserving (#3332) Commit f28fc22 introduced const qualifiers on post-(inc-/dec-)rement operators of iterators. These qualifiers prevent the use of basic_json in place of std::ranges::range, which requires the post-increment operator to be equality-preserving. These changes appear to be the result of ICC compiler suggestions, and no further explanation is discernible from the PR discussion (#858). Further testing revealed, that clang-tidy also suggests adding const to prevent "accidental mutation of a temporary object". As an alternative, this commit partially reverts f28fc22, removing all added const qualifiers from return types and adds lvalue reference qualifiers to the operator member functions instead. Unit tests ensure the operators remain equality-preserving and accidental mutation of temporaries following post-(inc-/dec-)rement is prohibited. Fixes #3331. --- .../nlohmann/detail/iterators/iter_impl.hpp | 4 +- .../iterators/json_reverse_iterator.hpp | 4 +- .../detail/iterators/primitive_iterator.hpp | 4 +- single_include/nlohmann/json.hpp | 12 +-- test/src/unit-class_iterator.cpp | 91 +++++++++++++++++++ 5 files changed, 103 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 434a62d3e..d8060786e 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -352,7 +352,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl const operator++(int) // NOLINT(readability-const-return-type) + iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp) { auto result = *this; ++(*this); @@ -403,7 +403,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl const operator--(int) // NOLINT(readability-const-return-type) + iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp) { auto result = *this; --(*this); diff --git a/include/nlohmann/detail/iterators/json_reverse_iterator.hpp b/include/nlohmann/detail/iterators/json_reverse_iterator.hpp index e787fdbcd..65bb327a5 100644 --- a/include/nlohmann/detail/iterators/json_reverse_iterator.hpp +++ b/include/nlohmann/detail/iterators/json_reverse_iterator.hpp @@ -48,7 +48,7 @@ class json_reverse_iterator : public std::reverse_iterator explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} /// post-increment (it++) - json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp) { return static_cast(base_iterator::operator++(1)); } @@ -60,7 +60,7 @@ class json_reverse_iterator : public std::reverse_iterator } /// post-decrement (it--) - json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp) { return static_cast(base_iterator::operator--(1)); } diff --git a/include/nlohmann/detail/iterators/primitive_iterator.hpp b/include/nlohmann/detail/iterators/primitive_iterator.hpp index 15aa2f08a..03bc37e2c 100644 --- a/include/nlohmann/detail/iterators/primitive_iterator.hpp +++ b/include/nlohmann/detail/iterators/primitive_iterator.hpp @@ -87,7 +87,7 @@ class primitive_iterator_t return *this; } - primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp) { auto result = *this; ++m_it; @@ -100,7 +100,7 @@ class primitive_iterator_t return *this; } - primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp) { auto result = *this; --m_it; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 48999920d..05000fbba 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11308,7 +11308,7 @@ class primitive_iterator_t return *this; } - primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp) { auto result = *this; ++m_it; @@ -11321,7 +11321,7 @@ class primitive_iterator_t return *this; } - primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp) { auto result = *this; --m_it; @@ -11728,7 +11728,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl const operator++(int) // NOLINT(readability-const-return-type) + iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp) { auto result = *this; ++(*this); @@ -11779,7 +11779,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl const operator--(int) // NOLINT(readability-const-return-type) + iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp) { auto result = *this; --(*this); @@ -12167,7 +12167,7 @@ class json_reverse_iterator : public std::reverse_iterator explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} /// post-increment (it++) - json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp) { return static_cast(base_iterator::operator++(1)); } @@ -12179,7 +12179,7 @@ class json_reverse_iterator : public std::reverse_iterator } /// post-decrement (it--) - json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp) { return static_cast(base_iterator::operator--(1)); } diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 0e159fc38..e3a972f97 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -33,6 +33,12 @@ SOFTWARE. #include using nlohmann::json; +template +using can_post_increment_temporary = decltype((std::declval()++)++); + +template +using can_post_decrement_temporary = decltype((std::declval()--)--); + TEST_CASE("iterator class") { SECTION("construction") @@ -399,4 +405,89 @@ TEST_CASE("iterator class") } } } + SECTION("equality-preserving") + { + SECTION("post-increment") + { + SECTION("primitive_iterator_t") + { + using Iter = nlohmann::detail::primitive_iterator_t; + CHECK(std::is_same < decltype(std::declval()++), Iter >::value); + } + SECTION("iter_impl") + { + using Iter = nlohmann::detail::iter_impl; + CHECK(std::is_same < decltype(std::declval()++), Iter >::value); + } + SECTION("json_reverse_iterator") + { + using Base = nlohmann::detail::iter_impl; + using Iter = nlohmann::detail::json_reverse_iterator; + CHECK(std::is_same < decltype(std::declval()++), Iter >::value); + } + } + SECTION("post-decrement") + { + SECTION("primitive_iterator_t") + { + using Iter = nlohmann::detail::primitive_iterator_t; + CHECK(std::is_same < decltype(std::declval()--), Iter >::value); + } + SECTION("iter_impl") + { + using Iter = nlohmann::detail::iter_impl; + CHECK(std::is_same < decltype(std::declval()--), Iter >::value ); + } + SECTION("json_reverse_iterator") + { + using Base = nlohmann::detail::iter_impl; + using Iter = nlohmann::detail::json_reverse_iterator; + CHECK(std::is_same < decltype(std::declval()--), Iter >::value ); + } + } + } + // prevent "accidental mutation of a temporary object" + SECTION("cert-dcl21-cpp") + { + using nlohmann::detail::is_detected; + SECTION("post-increment") + { + SECTION("primitive_iterator_t") + { + using Iter = nlohmann::detail::primitive_iterator_t; + CHECK_FALSE(is_detected::value); + } + SECTION("iter_impl") + { + using Iter = nlohmann::detail::iter_impl; + CHECK_FALSE(is_detected::value); + } + SECTION("json_reverse_iterator") + { + using Base = nlohmann::detail::iter_impl; + using Iter = nlohmann::detail::json_reverse_iterator; + CHECK_FALSE(is_detected::value); + } + } + SECTION("post-decrement") + { + SECTION("primitive_iterator_t") + { + using Iter = nlohmann::detail::primitive_iterator_t; + CHECK_FALSE(is_detected::value); + } + SECTION("iter_impl") + { + using Iter = nlohmann::detail::iter_impl; + CHECK_FALSE(is_detected::value); + } + SECTION("json_reverse_iterator") + { + using Base = nlohmann::detail::iter_impl; + using Iter = nlohmann::detail::json_reverse_iterator; + CHECK_FALSE(is_detected::value); + } + + } + } } From ad103e5b454a7e9ae2170d2cfdba5aa60979c9cd Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 24 Mar 2022 07:54:07 +0100 Subject: [PATCH 026/110] Improve unit testing (Part 1) (#3380) * Refactor unit test creation Add functions for creating tests and to supply test- and standard-specific build settings. Raises minimum CMake version to 3.13 in test directory. json_test_add_test_for( MAIN
[CXX_STANDARDS ...] [FORCE]) Given a unit-foo.cpp, produces test-foo_cpp if C++ standard is supported by the compiler and thesource file contains JSON_HAS_CPP_. Use FORCE to create the test regardless of the file containing JSON_HAS_CPP_. Test targets are linked against
. CXX_STANDARDS defaults to "11". json_test_set_test_options( all| [CXX_STANDARDS all|...] [COMPILE_DEFINITIONS ...] [COMPILE_FEATURES ...] [COMPILE_OPTIONS ...] [LINK_LIBRARIES ...] [LINK_OPTIONS ...]) Supply test- and standard-specific build settings. Specify multiple tests using a list e.g., "test-foo;test-bar". Must be called BEFORE the test is created. * Use CMAKE_MODULE_PATH * Don't undef some macros if JSON_TEST_KEEP_MACROS is defined * Use JSON_TEST_KEEP_MACROS Incidentally enables the regression tests for #2546 and #3070. A CHECK_THROWS_WITH_AS in #3070 was disabled which is tracked in #3377 and a line in from_json(..., std_fs::path&) was marked with LCOV_EXCL_LINE. * Add three-way comparison feature test macro * Disable broken comparison if JSON_HAS_THREE_WAY_COMPARISON * Fix redefinition of inline constexpr statics Redelcaration of inline constexpr static data members in namespace scope was deprecated in C++17. Fixes -Werror=deprecated compilation failures. * Fix more test build failures due to missing noexcept * CI: update cmake_flags test to use CMake 3.13 in test directory Also change default for JSON_BuildTests option to depend on CMake version. * CI: turn *_CXXFLAGS into CMake lists * CI: use JSON_TestStandards to set CXX_STANDARD * CI: pass extra CXXFLAGS to standards tests --- .github/workflows/macos.yml | 2 +- CMakeLists.txt | 11 +- benchmarks/CMakeLists.txt | 2 +- cmake/ci.cmake | 703 +++++++++--------- cmake/test.cmake | 204 +++++ .../nlohmann/detail/conversions/from_json.hpp | 3 +- include/nlohmann/detail/macro_scope.hpp | 9 + include/nlohmann/detail/macro_unscope.hpp | 20 +- include/nlohmann/detail/meta/cpp_future.hpp | 8 +- single_include/nlohmann/json.hpp | 40 +- test/CMakeLists.txt | 230 +++--- test/src/unit-alt-string.cpp | 6 +- test/src/unit-cbor.cpp | 2 +- test/src/unit-class_parser.cpp | 28 +- test/src/unit-comparison.cpp | 6 +- test/src/unit-conversions.cpp | 7 - test/src/unit-items.cpp | 7 - test/src/unit-regression1.cpp | 4 - test/src/unit-regression2.cpp | 67 +- test/src/unit-ubjson.cpp | 4 +- 20 files changed, 756 insertions(+), 607 deletions(-) create mode 100644 cmake/test.cmake diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6a378f13f..d01502bf5 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: cmake - run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DCMAKE_CXX_STANDARD_REQUIRED=ON + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_TestStandards=${{ matrix.standard }} - name: build run: cmake --build build --parallel 10 - name: test diff --git a/CMakeLists.txt b/CMakeLists.txt index b93c6e47f..68bba9cf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ endif() ## INCLUDE ## ## +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) include(ExternalProject) ## @@ -30,7 +31,13 @@ if (POLICY CMP0077) cmake_policy(SET CMP0077 NEW) endif () -option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${MAIN_PROJECT}) +# VERSION_GREATER_EQUAL is not available in CMake 3.1 +if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13)) + set(JSON_BuildTests_INIT ON) +else() + set(JSON_BuildTests_INIT OFF) +endif() +option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT}) option(JSON_CI "Enable CI build targets." OFF) option(JSON_Diagnostics "Use extended diagnostic messages." OFF) option(JSON_ImplicitConversions "Enable implicit conversions." ON) @@ -39,7 +46,7 @@ option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OF option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF) if (JSON_CI) - include(cmake/ci.cmake) + include(ci) endif () ## diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 1243f54ea..325904d12 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -23,7 +23,7 @@ if(NOT benchmark_POPULATED) endif() # download test data -include(${CMAKE_SOURCE_DIR}/../cmake/download_test_data.cmake) +include(download_test_data) # benchmark binary add_executable(json_benchmarks src/benchmarks.cpp) diff --git a/cmake/ci.cmake b/cmake/ci.cmake index 9a8136165..b9078b625 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -98,18 +98,18 @@ file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp) # -Wno-weak-vtables The library is header-only. # -Wreserved-identifier See https://github.com/onqtam/doctest/issues/536. -set(CLANG_CXXFLAGS "-std=c++11 \ - -Werror \ - -Weverything \ - -Wno-c++98-compat \ - -Wno-c++98-compat-pedantic \ - -Wno-deprecated-declarations \ - -Wno-extra-semi-stmt \ - -Wno-padded \ - -Wno-covered-switch-default \ - -Wno-weak-vtables \ - -Wno-reserved-identifier \ -") +set(CLANG_CXXFLAGS + -Werror + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-deprecated-declarations + -Wno-extra-semi-stmt + -Wno-padded + -Wno-covered-switch-default + -Wno-weak-vtables + -Wno-reserved-identifier +) # Warning flags determined for GCC 12.0 (experimental) with https://github.com/nlohmann/gcc_flags: # Ignored GCC warnings: @@ -121,295 +121,295 @@ set(CLANG_CXXFLAGS "-std=c++11 \ # -Wno-system-headers We do not care about warnings in system headers. # -Wno-templates The library uses templates. -set(GCC_CXXFLAGS "-std=c++11 \ - -pedantic \ - -Werror \ - --all-warnings \ - --extra-warnings \ - -W \ - -WNSObject-attribute \ - -Wno-abi-tag \ - -Waddress \ - -Waddress-of-packed-member \ - -Wno-aggregate-return \ - -Waggressive-loop-optimizations \ - -Waligned-new=all \ - -Wall \ - -Walloc-zero \ - -Walloca \ - -Wanalyzer-double-fclose \ - -Wanalyzer-double-free \ - -Wanalyzer-exposure-through-output-file \ - -Wanalyzer-file-leak \ - -Wanalyzer-free-of-non-heap \ - -Wanalyzer-malloc-leak \ - -Wanalyzer-mismatching-deallocation \ - -Wanalyzer-null-argument \ - -Wanalyzer-null-dereference \ - -Wanalyzer-possible-null-argument \ - -Wanalyzer-possible-null-dereference \ - -Wanalyzer-shift-count-negative \ - -Wanalyzer-shift-count-overflow \ - -Wanalyzer-stale-setjmp-buffer \ - -Wanalyzer-tainted-allocation-size \ - -Wanalyzer-tainted-array-index \ - -Wanalyzer-tainted-divisor \ - -Wanalyzer-tainted-offset \ - -Wanalyzer-tainted-size \ - -Wanalyzer-too-complex \ - -Wanalyzer-unsafe-call-within-signal-handler \ - -Wanalyzer-use-after-free \ - -Wanalyzer-use-of-pointer-in-stale-stack-frame \ - -Wanalyzer-use-of-uninitialized-value \ - -Wanalyzer-write-to-const \ - -Wanalyzer-write-to-string-literal \ - -Warith-conversion \ - -Warray-bounds \ - -Warray-bounds=2 \ - -Warray-compare \ - -Warray-parameter=2 \ - -Wattribute-alias=2 \ - -Wattribute-warning \ - -Wattributes \ - -Wbool-compare \ - -Wbool-operation \ - -Wbuiltin-declaration-mismatch \ - -Wbuiltin-macro-redefined \ - -Wc++0x-compat \ - -Wc++11-compat \ - -Wc++11-extensions \ - -Wc++14-compat \ - -Wc++14-extensions \ - -Wc++17-compat \ - -Wc++17-extensions \ - -Wc++1z-compat \ - -Wc++20-compat \ - -Wc++20-extensions \ - -Wc++23-extensions \ - -Wc++2a-compat \ - -Wcannot-profile \ - -Wcast-align \ - -Wcast-align=strict \ - -Wcast-function-type \ - -Wcast-qual \ - -Wcatch-value=3 \ - -Wchar-subscripts \ - -Wclass-conversion \ - -Wclass-memaccess \ - -Wclobbered \ - -Wcomma-subscript \ - -Wcomment \ - -Wcomments \ - -Wconditionally-supported \ - -Wconversion \ - -Wconversion-null \ - -Wcoverage-invalid-line-number \ - -Wcoverage-mismatch \ - -Wcpp \ - -Wctad-maybe-unsupported \ - -Wctor-dtor-privacy \ - -Wdangling-else \ - -Wdate-time \ - -Wdelete-incomplete \ - -Wdelete-non-virtual-dtor \ - -Wdeprecated \ - -Wdeprecated-copy \ - -Wdeprecated-copy-dtor \ - -Wdeprecated-declarations \ - -Wdeprecated-enum-enum-conversion \ - -Wdeprecated-enum-float-conversion \ - -Wdisabled-optimization \ - -Wdiv-by-zero \ - -Wdouble-promotion \ - -Wduplicated-branches \ - -Wduplicated-cond \ - -Weffc++ \ - -Wempty-body \ - -Wendif-labels \ - -Wenum-compare \ - -Wenum-conversion \ - -Wexceptions \ - -Wexpansion-to-defined \ - -Wextra \ - -Wextra-semi \ - -Wfloat-conversion \ - -Wfloat-equal \ - -Wformat-diag \ - -Wformat-overflow=2 \ - -Wformat-signedness \ - -Wformat-truncation=2 \ - -Wformat=2 \ - -Wframe-address \ - -Wfree-nonheap-object \ - -Whsa \ - -Wif-not-aligned \ - -Wignored-attributes \ - -Wignored-qualifiers \ - -Wimplicit-fallthrough=5 \ - -Winaccessible-base \ - -Winfinite-recursion \ - -Winherited-variadic-ctor \ - -Winit-list-lifetime \ - -Winit-self \ - -Winline \ - -Wint-in-bool-context \ - -Wint-to-pointer-cast \ - -Winterference-size \ - -Winvalid-imported-macros \ - -Winvalid-memory-model \ - -Winvalid-offsetof \ - -Winvalid-pch \ - -Wliteral-suffix \ - -Wlogical-not-parentheses \ - -Wlogical-op \ - -Wno-long-long \ - -Wlto-type-mismatch \ - -Wmain \ - -Wmaybe-uninitialized \ - -Wmemset-elt-size \ - -Wmemset-transposed-args \ - -Wmisleading-indentation \ - -Wmismatched-dealloc \ - -Wmismatched-new-delete \ - -Wmismatched-tags \ - -Wmissing-attributes \ - -Wmissing-braces \ - -Wmissing-declarations \ - -Wmissing-field-initializers \ - -Wmissing-include-dirs \ - -Wmissing-profile \ - -Wmissing-requires \ - -Wmultichar \ - -Wmultiple-inheritance \ - -Wmultistatement-macros \ - -Wno-namespaces \ - -Wnarrowing \ - -Wnoexcept \ - -Wnoexcept-type \ - -Wnon-template-friend \ - -Wnon-virtual-dtor \ - -Wnonnull \ - -Wnonnull-compare \ - -Wnormalized=nfkc \ - -Wnull-dereference \ - -Wodr \ - -Wold-style-cast \ - -Wopenacc-parallelism \ - -Wopenmp-simd \ - -Woverflow \ - -Woverlength-strings \ - -Woverloaded-virtual \ - -Wpacked \ - -Wpacked-bitfield-compat \ - -Wpacked-not-aligned \ - -Wno-padded \ - -Wparentheses \ - -Wpedantic \ - -Wpessimizing-move \ - -Wplacement-new=2 \ - -Wpmf-conversions \ - -Wpointer-arith \ - -Wpointer-compare \ - -Wpragmas \ - -Wprio-ctor-dtor \ - -Wpsabi \ - -Wrange-loop-construct \ - -Wredundant-decls \ - -Wredundant-move \ - -Wredundant-tags \ - -Wregister \ - -Wreorder \ - -Wrestrict \ - -Wreturn-local-addr \ - -Wreturn-type \ - -Wscalar-storage-order \ - -Wsequence-point \ - -Wshadow=compatible-local \ - -Wshadow=global \ - -Wshadow=local \ - -Wshift-count-negative \ - -Wshift-count-overflow \ - -Wshift-negative-value \ - -Wshift-overflow=2 \ - -Wsign-compare \ - -Wsign-conversion \ - -Wsign-promo \ - -Wsized-deallocation \ - -Wsizeof-array-argument \ - -Wsizeof-array-div \ - -Wsizeof-pointer-div \ - -Wsizeof-pointer-memaccess \ - -Wstack-protector \ - -Wstrict-aliasing \ - -Wstrict-aliasing=3 \ - -Wstrict-null-sentinel \ - -Wstrict-overflow \ - -Wstrict-overflow=5 \ - -Wstring-compare \ - -Wstringop-overflow=4 \ - -Wstringop-overread \ - -Wstringop-truncation \ - -Wsubobject-linkage \ - -Wsuggest-attribute=cold \ - -Wsuggest-attribute=const \ - -Wsuggest-attribute=format \ - -Wsuggest-attribute=malloc \ - -Wsuggest-attribute=noreturn \ - -Wsuggest-attribute=pure \ - -Wsuggest-final-methods \ - -Wsuggest-final-types \ - -Wsuggest-override \ - -Wswitch \ - -Wswitch-bool \ - -Wswitch-default \ - -Wswitch-enum \ - -Wswitch-outside-range \ - -Wswitch-unreachable \ - -Wsync-nand \ - -Wsynth \ - -Wno-system-headers \ - -Wtautological-compare \ - -Wno-templates \ - -Wterminate \ - -Wtrampolines \ - -Wtrigraphs \ - -Wtsan \ - -Wtype-limits \ - -Wundef \ - -Wuninitialized \ - -Wunknown-pragmas \ - -Wunreachable-code \ - -Wunsafe-loop-optimizations \ - -Wunused \ - -Wunused-but-set-parameter \ - -Wunused-but-set-variable \ - -Wunused-const-variable=2 \ - -Wunused-function \ - -Wunused-label \ - -Wunused-local-typedefs \ - -Wunused-macros \ - -Wunused-parameter \ - -Wunused-result \ - -Wunused-value \ - -Wunused-variable \ - -Wuseless-cast \ - -Wvarargs \ - -Wvariadic-macros \ - -Wvector-operation-performance \ - -Wvexing-parse \ - -Wvirtual-inheritance \ - -Wvirtual-move-assign \ - -Wvla \ - -Wvla-parameter \ - -Wvolatile \ - -Wvolatile-register-var \ - -Wwrite-strings \ - -Wzero-as-null-pointer-constant \ - -Wzero-length-bounds \ -") +set(GCC_CXXFLAGS + -pedantic + -Werror + --all-warnings + --extra-warnings + -W + -WNSObject-attribute + -Wno-abi-tag + -Waddress + -Waddress-of-packed-member + -Wno-aggregate-return + -Waggressive-loop-optimizations + -Waligned-new=all + -Wall + -Walloc-zero + -Walloca + -Wanalyzer-double-fclose + -Wanalyzer-double-free + -Wanalyzer-exposure-through-output-file + -Wanalyzer-file-leak + -Wanalyzer-free-of-non-heap + -Wanalyzer-malloc-leak + -Wanalyzer-mismatching-deallocation + -Wanalyzer-null-argument + -Wanalyzer-null-dereference + -Wanalyzer-possible-null-argument + -Wanalyzer-possible-null-dereference + -Wanalyzer-shift-count-negative + -Wanalyzer-shift-count-overflow + -Wanalyzer-stale-setjmp-buffer + -Wanalyzer-tainted-allocation-size + -Wanalyzer-tainted-array-index + -Wanalyzer-tainted-divisor + -Wanalyzer-tainted-offset + -Wanalyzer-tainted-size + -Wanalyzer-too-complex + -Wanalyzer-unsafe-call-within-signal-handler + -Wanalyzer-use-after-free + -Wanalyzer-use-of-pointer-in-stale-stack-frame + -Wanalyzer-use-of-uninitialized-value + -Wanalyzer-write-to-const + -Wanalyzer-write-to-string-literal + -Warith-conversion + -Warray-bounds + -Warray-bounds=2 + -Warray-compare + -Warray-parameter=2 + -Wattribute-alias=2 + -Wattribute-warning + -Wattributes + -Wbool-compare + -Wbool-operation + -Wbuiltin-declaration-mismatch + -Wbuiltin-macro-redefined + -Wc++0x-compat + -Wc++11-compat + -Wc++11-extensions + -Wc++14-compat + -Wc++14-extensions + -Wc++17-compat + -Wc++17-extensions + -Wc++1z-compat + -Wc++20-compat + -Wc++20-extensions + -Wc++23-extensions + -Wc++2a-compat + -Wcannot-profile + -Wcast-align + -Wcast-align=strict + -Wcast-function-type + -Wcast-qual + -Wcatch-value=3 + -Wchar-subscripts + -Wclass-conversion + -Wclass-memaccess + -Wclobbered + -Wcomma-subscript + -Wcomment + -Wcomments + -Wconditionally-supported + -Wconversion + -Wconversion-null + -Wcoverage-invalid-line-number + -Wcoverage-mismatch + -Wcpp + -Wctad-maybe-unsupported + -Wctor-dtor-privacy + -Wdangling-else + -Wdate-time + -Wdelete-incomplete + -Wdelete-non-virtual-dtor + -Wdeprecated + -Wdeprecated-copy + -Wdeprecated-copy-dtor + -Wdeprecated-declarations + -Wdeprecated-enum-enum-conversion + -Wdeprecated-enum-float-conversion + -Wdisabled-optimization + -Wdiv-by-zero + -Wdouble-promotion + -Wduplicated-branches + -Wduplicated-cond + -Weffc++ + -Wempty-body + -Wendif-labels + -Wenum-compare + -Wenum-conversion + -Wexceptions + -Wexpansion-to-defined + -Wextra + -Wextra-semi + -Wfloat-conversion + -Wfloat-equal + -Wformat-diag + -Wformat-overflow=2 + -Wformat-signedness + -Wformat-truncation=2 + -Wformat=2 + -Wframe-address + -Wfree-nonheap-object + -Whsa + -Wif-not-aligned + -Wignored-attributes + -Wignored-qualifiers + -Wimplicit-fallthrough=5 + -Winaccessible-base + -Winfinite-recursion + -Winherited-variadic-ctor + -Winit-list-lifetime + -Winit-self + -Winline + -Wint-in-bool-context + -Wint-to-pointer-cast + -Winterference-size + -Winvalid-imported-macros + -Winvalid-memory-model + -Winvalid-offsetof + -Winvalid-pch + -Wliteral-suffix + -Wlogical-not-parentheses + -Wlogical-op + -Wno-long-long + -Wlto-type-mismatch + -Wmain + -Wmaybe-uninitialized + -Wmemset-elt-size + -Wmemset-transposed-args + -Wmisleading-indentation + -Wmismatched-dealloc + -Wmismatched-new-delete + -Wmismatched-tags + -Wmissing-attributes + -Wmissing-braces + -Wmissing-declarations + -Wmissing-field-initializers + -Wmissing-include-dirs + -Wmissing-profile + -Wmissing-requires + -Wmultichar + -Wmultiple-inheritance + -Wmultistatement-macros + -Wno-namespaces + -Wnarrowing + -Wnoexcept + -Wnoexcept-type + -Wnon-template-friend + -Wnon-virtual-dtor + -Wnonnull + -Wnonnull-compare + -Wnormalized=nfkc + -Wnull-dereference + -Wodr + -Wold-style-cast + -Wopenacc-parallelism + -Wopenmp-simd + -Woverflow + -Woverlength-strings + -Woverloaded-virtual + -Wpacked + -Wpacked-bitfield-compat + -Wpacked-not-aligned + -Wno-padded + -Wparentheses + -Wpedantic + -Wpessimizing-move + -Wplacement-new=2 + -Wpmf-conversions + -Wpointer-arith + -Wpointer-compare + -Wpragmas + -Wprio-ctor-dtor + -Wpsabi + -Wrange-loop-construct + -Wredundant-decls + -Wredundant-move + -Wredundant-tags + -Wregister + -Wreorder + -Wrestrict + -Wreturn-local-addr + -Wreturn-type + -Wscalar-storage-order + -Wsequence-point + -Wshadow=compatible-local + -Wshadow=global + -Wshadow=local + -Wshift-count-negative + -Wshift-count-overflow + -Wshift-negative-value + -Wshift-overflow=2 + -Wsign-compare + -Wsign-conversion + -Wsign-promo + -Wsized-deallocation + -Wsizeof-array-argument + -Wsizeof-array-div + -Wsizeof-pointer-div + -Wsizeof-pointer-memaccess + -Wstack-protector + -Wstrict-aliasing + -Wstrict-aliasing=3 + -Wstrict-null-sentinel + -Wstrict-overflow + -Wstrict-overflow=5 + -Wstring-compare + -Wstringop-overflow=4 + -Wstringop-overread + -Wstringop-truncation + -Wsubobject-linkage + -Wsuggest-attribute=cold + -Wsuggest-attribute=const + -Wsuggest-attribute=format + -Wsuggest-attribute=malloc + -Wsuggest-attribute=noreturn + -Wsuggest-attribute=pure + -Wsuggest-final-methods + -Wsuggest-final-types + -Wsuggest-override + -Wswitch + -Wswitch-bool + -Wswitch-default + -Wswitch-enum + -Wswitch-outside-range + -Wswitch-unreachable + -Wsync-nand + -Wsynth + -Wno-system-headers + -Wtautological-compare + -Wno-templates + -Wterminate + -Wtrampolines + -Wtrigraphs + -Wtsan + -Wtype-limits + -Wundef + -Wuninitialized + -Wunknown-pragmas + -Wunreachable-code + -Wunsafe-loop-optimizations + -Wunused + -Wunused-but-set-parameter + -Wunused-but-set-variable + -Wunused-const-variable=2 + -Wunused-function + -Wunused-label + -Wunused-local-typedefs + -Wunused-macros + -Wunused-parameter + -Wunused-result + -Wunused-value + -Wunused-variable + -Wuseless-cast + -Wvarargs + -Wvariadic-macros + -Wvector-operation-performance + -Wvexing-parse + -Wvirtual-inheritance + -Wvirtual-move-assign + -Wvla + -Wvla-parameter + -Wvolatile + -Wvolatile-register-var + -Wwrite-strings + -Wzero-as-null-pointer-constant + -Wzero-length-bounds +) add_custom_target(ci_test_gcc - COMMAND CXX=${GCC_TOOL} CXXFLAGS=${GCC_CXXFLAGS} ${CMAKE_COMMAND} + COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_gcc @@ -419,7 +419,7 @@ add_custom_target(ci_test_gcc ) add_custom_target(ci_test_clang - COMMAND CXX=${CLANG_TOOL} CXXFLAGS=${CLANG_CXXFLAGS} ${CMAKE_COMMAND} + COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang @@ -434,10 +434,10 @@ add_custom_target(ci_test_clang foreach(CXX_STANDARD 11 14 17 20) add_custom_target(ci_test_gcc_cxx${CXX_STANDARD} - COMMAND CXX=${GCC_TOOL} ${CMAKE_COMMAND} + COMMAND CXX=${GCC_TOOL} CXXFLAGS="${GCC_CXXFLAGS}" ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja - -DCMAKE_CXX_STANDARD=${CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=ON -DJSON_BuildTests=ON -DJSON_FastTests=ON + -DJSON_TestStandards=${CXX_STANDARD} -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_gcc_cxx${CXX_STANDARD} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_gcc_cxx${CXX_STANDARD} COMMAND cd ${PROJECT_BINARY_DIR}/build_gcc_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure @@ -445,10 +445,10 @@ foreach(CXX_STANDARD 11 14 17 20) ) add_custom_target(ci_test_clang_cxx${CXX_STANDARD} - COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND} + COMMAND CXX=${CLANG_TOOL} CXXFLAGS="${CLANG_CXXFLAGS}" ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja - -DCMAKE_CXX_STANDARD=${CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=ON - -DJSON_BuildTests=ON + -DJSON_BuildTests=ON -DJSON_FastTests=ON + -DJSON_TestStandards=${CXX_STANDARD} -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} COMMAND cd ${PROJECT_BINARY_DIR}/build_clang_cxx${CXX_STANDARD} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure @@ -769,50 +769,67 @@ add_custom_target(ci_benchmarks # CMake flags ############################################################################### -if (APPLE) - set(CMAKE_310_BINARY ${PROJECT_BINARY_DIR}/cmake-3.1.0-Darwin64/CMake.app/Contents/bin/cmake) - add_custom_command( - OUTPUT ${CMAKE_310_BINARY} - COMMAND wget https://github.com/Kitware/CMake/releases/download/v3.1.0/cmake-3.1.0-Darwin64.tar.gz - COMMAND tar xfz cmake-3.1.0-Darwin64.tar.gz - COMMAND rm cmake-3.1.0-Darwin64.tar.gz - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMENT "Download CMake 3.1.0" - ) -else() - set(CMAKE_310_BINARY ${PROJECT_BINARY_DIR}/cmake-3.1.0-Linux-x86_64/bin/cmake) - add_custom_command( - OUTPUT ${CMAKE_310_BINARY} - COMMAND wget https://github.com/Kitware/CMake/releases/download/v3.1.0/cmake-3.1.0-Linux-x86_64.tar.gz - COMMAND tar xfz cmake-3.1.0-Linux-x86_64.tar.gz - COMMAND rm cmake-3.1.0-Linux-x86_64.tar.gz - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMENT "Download CMake 3.1.0" - ) -endif() +function(ci_get_cmake version var) + if (APPLE) + set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}-Darwin64/CMake.app/Contents/bin/cmake) + add_custom_command( + OUTPUT ${${var}} + COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-Darwin64.tar.gz + COMMAND tar xfz cmake-${version}-Darwin64.tar.gz + COMMAND rm cmake-${version}-Darwin64.tar.gz + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Download CMake ${version}" + ) + else() + set(${var} ${PROJECT_BINARY_DIR}/cmake-${version}-Linux-x86_64/bin/cmake) + add_custom_command( + OUTPUT ${${var}} + COMMAND wget -nc https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-Linux-x86_64.tar.gz + COMMAND tar xfz cmake-${version}-Linux-x86_64.tar.gz + COMMAND rm cmake-${version}-Linux-x86_64.tar.gz + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Download CMake ${version}" + ) + endif() + set(${var} ${${var}} PARENT_SCOPE) +endfunction() -set(JSON_CMAKE_FLAGS "JSON_BuildTests;JSON_Install;JSON_MultipleHeaders;JSON_ImplicitConversions;JSON_Valgrind;JSON_Diagnostics;JSON_SystemInclude") +ci_get_cmake(3.1.0 CMAKE_3_1_0_BINARY) +ci_get_cmake(3.13.0 CMAKE_3_13_0_BINARY) -foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS}) - string(TOLOWER "ci_cmake_flag_${JSON_CMAKE_FLAG}" JSON_CMAKE_FLAG_TARGET) - add_custom_target("${JSON_CMAKE_FLAG_TARGET}" - COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake ${CMAKE_VERSION})" +set(JSON_CMAKE_FLAGS_3_1_0 "JSON_Install;JSON_MultipleHeaders;JSON_ImplicitConversions;JSON_Valgrind;JSON_Diagnostics;JSON_SystemInclude") +set(JSON_CMAKE_FLAGS_3_13_0 "JSON_BuildTests") + +function(ci_add_cmake_flags_targets flag min_version) + string(TOLOWER "ci_cmake_flag_${flag}" flag_target) + string(REPLACE . _ min_version_var ${min_version}) + set(cmake_binary ${CMAKE_${min_version_var}_BINARY}) + add_custom_target(${flag_target} + COMMENT "Check CMake flag ${flag} (CMake ${CMAKE_VERSION})" COMMAND ${CMAKE_COMMAND} -Werror=dev - -D${JSON_CMAKE_FLAG}=ON - -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_${JSON_CMAKE_FLAG_TARGET} + -D${flag}=ON + -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_${flag_target} ) - add_custom_target("${JSON_CMAKE_FLAG_TARGET}_31" - COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake 3.1)" - COMMAND mkdir ${PROJECT_BINARY_DIR}/build_${JSON_CMAKE_FLAG_TARGET}_31 - COMMAND cd ${PROJECT_BINARY_DIR}/build_${JSON_CMAKE_FLAG_TARGET}_31 && ${CMAKE_310_BINARY} - -Werror=dev ${PROJECT_SOURCE_DIR} - -D${JSON_CMAKE_FLAG}=ON - -DCMAKE_CXX_COMPILE_FEATURES="cxx_range_for" -DCMAKE_CXX_FLAGS="-std=gnu++11" - DEPENDS ${CMAKE_310_BINARY} + add_custom_target(${flag_target}_${min_version_var} + COMMENT "Check CMake flag ${JSON_CMAKE_FLAG} (CMake ${min_version})" + COMMAND mkdir -pv ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var} + COMMAND cd ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var} + && ${cmake_binary} -Werror=dev ${PROJECT_SOURCE_DIR} -D${flag}=ON + DEPENDS ${cmake_binary} ) - list(APPEND JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGET} ${JSON_CMAKE_FLAG_TARGET}_31) - list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${JSON_CMAKE_FLAG_TARGET} ${PROJECT_BINARY_DIR}/build_${JSON_CMAKE_FLAG_TARGET}_31) + list(APPEND JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGET} ${flag_target}_${min_version_var}) + list(APPEND JSON_CMAKE_FLAG_BUILD_DIRS ${PROJECT_BINARY_DIR}/build_${flag_target} ${PROJECT_BINARY_DIR}/build_${flag_target}_${min_version_var}) + set(JSON_CMAKE_FLAG_TARGETS ${JSON_CMAKE_FLAG_TARGETS} PARENT_SCOPE) + set(JSON_CMAKE_FLAG_BUILD_DIRS ${JSON_CMAKE_FLAG_BUILD_DIRS} PARENT_SCOPE) +endfunction() + +foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_3_1_0}) + ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 3.1.0) +endforeach() + +foreach(JSON_CMAKE_FLAG ${JSON_CMAKE_FLAGS_3_13_0}) + ci_add_cmake_flags_targets(${JSON_CMAKE_FLAG} 3.13.0) endforeach() add_custom_target(ci_cmake_flags diff --git a/cmake/test.cmake b/cmake/test.cmake new file mode 100644 index 000000000..1d146646b --- /dev/null +++ b/cmake/test.cmake @@ -0,0 +1,204 @@ +set(_json_test_cmake_list_file ${CMAKE_CURRENT_LIST_FILE}) + +############################################################################# +# download test data +############################################################################# + +include(download_test_data) + +# test fixture to download test data +add_test(NAME "download_test_data" COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} + --target download_test_data +) +set_tests_properties(download_test_data PROPERTIES FIXTURES_SETUP TEST_DATA) + +if(JSON_Valgrind) + find_program(CMAKE_MEMORYCHECK_COMMAND valgrind) + message(STATUS "Executing test suite with Valgrind (${CMAKE_MEMORYCHECK_COMMAND})") + set(memcheck_command "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS} --error-exitcode=1 --leak-check=full") + separate_arguments(memcheck_command) +endif() + +############################################################################# +# detect standard support +############################################################################# + +# C++11 is the minimum required +set(compiler_supports_cpp_11 TRUE) + +foreach(feature ${CMAKE_CXX_COMPILE_FEATURES}) + if (${feature} STREQUAL cxx_std_14) + set(compiler_supports_cpp_14 TRUE) + elseif (${feature} STREQUAL cxx_std_17) + set(compiler_supports_cpp_17 TRUE) + elseif (${feature} STREQUAL cxx_std_20) + set(compiler_supports_cpp_20 TRUE) + elseif (${feature} STREQUAL cxx_std_23) + set(compiler_supports_cpp_23 TRUE) + endif() +endforeach() + +############################################################################# +# test functions +############################################################################# + +############################################################################# +# json_test_set_test_options( +# all| +# [CXX_STANDARDS all|...] +# [COMPILE_DEFINITIONS ...] +# [COMPILE_FEATURES ...] +# [COMPILE_OPTIONS ...] +# [LINK_LIBRARIES ...] +# [LINK_OPTIONS ...]) +# +# Supply test- and standard-specific build settings. +# Specify multiple tests using a list e.g., "test-foo;test-bar". +# +# Must be called BEFORE the test is created. +############################################################################# + +function(json_test_set_test_options tests) + cmake_parse_arguments(args "" "" + "CXX_STANDARDS;COMPILE_DEFINITIONS;COMPILE_FEATURES;COMPILE_OPTIONS;LINK_LIBRARIES;LINK_OPTIONS" + ${ARGN}) + + if(NOT args_CXX_STANDARDS) + set(args_CXX_STANDARDS "all") + endif() + + foreach(test ${tests}) + if("${test}" STREQUAL "all") + set(test "") + endif() + + foreach(cxx_standard ${args_CXX_STANDARDS}) + if("${cxx_standard}" STREQUAL "all") + if("${test}" STREQUAL "") + message(FATAL_ERROR "Not supported. Change defaults in: ${_json_test_cmake_list_file}") + endif() + set(test_interface _json_test_interface_${test}) + else() + set(test_interface _json_test_interface_${test}_cpp_${cxx_standard}) + endif() + + if(NOT TARGET ${test_interface}) + add_library(${test_interface} INTERFACE) + endif() + + target_compile_definitions(${test_interface} INTERFACE ${args_COMPILE_DEFINITIONS}) + target_compile_features(${test_interface} INTERFACE ${args_COMPILE_FEATURES}) + target_compile_options(${test_interface} INTERFACE ${args_COMPILE_OPTIONS}) + target_link_libraries (${test_interface} INTERFACE ${args_LINK_LIBRARIES}) + target_link_options(${test_interface} INTERFACE ${args_LINK_OPTIONS}) + endforeach() + endforeach() +endfunction() + +# for internal use by json_test_add_test_for() +function(_json_test_add_test test_name file main cxx_standard) + set(test_target ${test_name}_cpp${cxx_standard}) + + if(TARGET ${test_target}) + message(FATAL_ERROR "Target ${test_target} has already been added.") + endif() + + add_executable(${test_target} ${file}) + target_link_libraries(${test_target} PRIVATE ${main}) + + # set and require C++ standard + set_target_properties(${test_target} PROPERTIES + CXX_STANDARD ${cxx_standard} + CXX_STANDARD_REQUIRED ON + ) + + # apply standard-specific build settings + if(TARGET _json_test_interface__cpp_${cxx_standard}) + target_link_libraries(${test_target} PRIVATE _json_test_interface__cpp_${cxx_standard}) + endif() + + # apply test-specific build settings + if(TARGET _json_test_interface_${test_name}) + target_link_libraries(${test_target} PRIVATE _json_test_interface_${test_name}) + endif() + + # apply test- and standard-specific build settings + if(TARGET _json_test_interface_${test_name}_cpp_${cxx_standard}) + target_link_libraries(${test_target} PRIVATE + _json_test_interface_${test_name}_cpp_${cxx_standard} + ) + endif() + + if (JSON_FastTests) + add_test(NAME ${test_target} + COMMAND ${test_target} ${DOCTEST_TEST_FILTER} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + else() + add_test(NAME ${test_target} + COMMAND ${test_target} ${DOCTEST_TEST_FILTER} --no-skip + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + endif() + set_tests_properties(${test_target} PROPERTIES LABELS "all" FIXTURES_REQUIRED TEST_DATA) + + if(JSON_Valgrind) + add_test(NAME ${test_target}_valgrind + COMMAND ${memcheck_command} $ ${DOCTEST_TEST_FILTER} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + set_tests_properties(${test_target}_valgrind PROPERTIES + LABELS "valgrind" FIXTURES_REQUIRED TEST_DATA + ) + endif() +endfunction() + +############################################################################# +# json_test_add_test_for( +# +# MAIN
+# [CXX_STANDARDS ...] [FORCE]) +# +# Given a unit-foo.cpp, produces +# +# test-foo_cpp +# +# if C++ standard is supported by the compiler and the +# source file contains JSON_HAS_CPP_. +# Use FORCE to create the test regardless of the file containing +# JSON_HAS_CPP_. +# Test targets are linked against
. +# CXX_STANDARDS defaults to "11". +############################################################################# + +function(json_test_add_test_for file) + cmake_parse_arguments(args "FORCE" "MAIN" "CXX_STANDARDS" ${ARGN}) + + get_filename_component(file_basename ${file} NAME_WE) + string(REGEX REPLACE "unit-([^$]+)" "test-\\1" test_name ${file_basename}) + + if("${args_MAIN}" STREQUAL "") + message(FATAL_ERROR "Required argument MAIN
missing.") + endif() + + if("${args_CXX_STANDARDS}" STREQUAL "") + set(args_CXX_STANDARDS 11) + endif() + + file(READ ${file} file_content) + foreach(cxx_standard ${args_CXX_STANDARDS}) + if(NOT compiler_supports_cpp_${cxx_standard}) + continue() + endif() + + # add unconditionally if C++11 (default) or forced + if(NOT ("${cxx_standard}" STREQUAL 11 OR args_FORCE)) + string(FIND "${file_content}" JSON_HAS_CPP_${cxx_standard} has_cpp_found) + if(${has_cpp_found} EQUAL -1) + continue() + endif() + endif() + + _json_test_add_test(${test_name} ${file} ${args_MAIN} ${cxx_standard}) + endforeach() +endfunction() diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 207d3e302..fbb176879 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -464,7 +464,8 @@ void from_json(const BasicJsonType& j, std_fs::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + // Not tested because of #3377 (related #3070) + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); // LCOV_EXCL_LINE } p = *j.template get_ptr(); } diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 87f964353..f636b908a 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -97,6 +97,15 @@ #define JSON_HAS_FILESYSTEM 0 #endif +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \ + && defined(__cpp_impl_three_way_comparison)&& __cpp_impl_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index 1a29fb5e0..377d3f11d 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -8,19 +8,23 @@ // clean up #undef JSON_ASSERT #undef JSON_INTERNAL_CATCH -#undef JSON_CATCH #undef JSON_THROW -#undef JSON_TRY #undef JSON_PRIVATE_UNLESS_TESTED -#undef JSON_HAS_CPP_11 -#undef JSON_HAS_CPP_14 -#undef JSON_HAS_CPP_17 -#undef JSON_HAS_CPP_20 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL +#ifndef JSON_TEST_KEEP_MACROS + #undef JSON_CATCH + #undef JSON_TRY + #undef JSON_HAS_CPP_11 + #undef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_20 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #undef JSON_HAS_THREE_WAY_COMPARISON +#endif + #include diff --git a/include/nlohmann/detail/meta/cpp_future.hpp b/include/nlohmann/detail/meta/cpp_future.hpp index 147f2fa35..ef2da370d 100644 --- a/include/nlohmann/detail/meta/cpp_future.hpp +++ b/include/nlohmann/detail/meta/cpp_future.hpp @@ -147,8 +147,12 @@ struct static_const static constexpr T value{}; }; -template -constexpr T static_const::value; // NOLINT(readability-redundant-declaration) +#ifndef JSON_HAS_CPP_17 + + template + constexpr T static_const::value; // NOLINT(readability-redundant-declaration) + +#endif } // namespace detail } // namespace nlohmann diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 05000fbba..71f6814ad 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2394,6 +2394,15 @@ using is_detected_convertible = #define JSON_HAS_FILESYSTEM 0 #endif +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \ + && defined(__cpp_impl_three_way_comparison)&& __cpp_impl_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push @@ -3182,8 +3191,12 @@ struct static_const static constexpr T value{}; }; -template -constexpr T static_const::value; // NOLINT(readability-redundant-declaration) +#ifndef JSON_HAS_CPP_17 + + template + constexpr T static_const::value; // NOLINT(readability-redundant-declaration) + +#endif } // namespace detail } // namespace nlohmann @@ -4266,7 +4279,8 @@ void from_json(const BasicJsonType& j, std_fs::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + // Not tested because of #3377 (related #3070) + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); // LCOV_EXCL_LINE } p = *j.template get_ptr(); } @@ -21939,21 +21953,25 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std // clean up #undef JSON_ASSERT #undef JSON_INTERNAL_CATCH -#undef JSON_CATCH #undef JSON_THROW -#undef JSON_TRY #undef JSON_PRIVATE_UNLESS_TESTED -#undef JSON_HAS_CPP_11 -#undef JSON_HAS_CPP_14 -#undef JSON_HAS_CPP_17 -#undef JSON_HAS_CPP_20 -#undef JSON_HAS_FILESYSTEM -#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL +#ifndef JSON_TEST_KEEP_MACROS + #undef JSON_CATCH + #undef JSON_TRY + #undef JSON_HAS_CPP_11 + #undef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_20 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #undef JSON_HAS_THREE_WAY_COMPARISON +#endif + // #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c29662134..90c325861 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,163 +1,131 @@ +cmake_minimum_required(VERSION 3.13) + option(JSON_Valgrind "Execute test suite with Valgrind." OFF) option(JSON_FastTests "Skip expensive/slow tests." OFF) -# download test data -include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/download_test_data.cmake) +set(JSON_TestStandards "" CACHE STRING "The list of standards to test explicitly.") -# test fixture to download test data -add_test(NAME "download_test_data" COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target download_test_data) -set_tests_properties(download_test_data PROPERTIES FIXTURES_SETUP TEST_DATA) +include(test) -if(JSON_Valgrind) - find_program(CMAKE_MEMORYCHECK_COMMAND valgrind) - message(STATUS "Executing test suite with Valgrind (${CMAKE_MEMORYCHECK_COMMAND})") - set(memcheck_command "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS} --error-exitcode=1 --leak-check=full") - separate_arguments(memcheck_command) +############################################################################# +# override standard support +############################################################################# + +# compiling json.hpp in C++14 mode fails with Clang <4.0 +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0) + unset(compiler_supports_cpp_14) endif() -############################################################################# -# doctest library with the main function to speed up build -############################################################################# - -add_library(doctest_main OBJECT src/unit.cpp) -set_target_properties(doctest_main PROPERTIES - COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" -) -if (${CMAKE_VERSION} VERSION_LESS "3.8.0") - target_compile_features(doctest_main PUBLIC cxx_range_for) -else() - target_compile_features(doctest_main PUBLIC cxx_std_11) -endif() -target_include_directories(doctest_main PRIVATE "thirdparty/doctest") - -# https://stackoverflow.com/questions/2368811/how-to-set-warning-level-in-cmake -if(MSVC) - # Force to always compile with W4 - if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") - string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") - endif() - - # Disable warning C4566: character represented by universal-character-name '\uFF01' cannot be represented in the current code page (1252) - # Disable warning C4996: 'nlohmann::basic_json::operator <<': was declared deprecated - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4566 /wd4996") - - # https://github.com/nlohmann/json/issues/1114 - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") -endif() - -############################################################################# -# one executable for each unit test file -############################################################################# - -# check if compiler supports C++17 -foreach(feature ${CMAKE_CXX_COMPILE_FEATURES}) - if (${feature} STREQUAL cxx_std_17) - set(compiler_supports_cpp_17 TRUE) - endif() -endforeach() # Clang only supports C++17 starting from Clang 5.0 -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) unset(compiler_supports_cpp_17) endif() # MSVC 2015 (14.0) does not support C++17 -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.1) +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.1) unset(compiler_supports_cpp_17) endif() -file(GLOB files src/unit-*.cpp) +# Clang C++20 support appears insufficient prior to Clang 9.0 (based on CI build failure) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + unset(compiler_supports_cpp_20) +endif() +# GCC started supporting C++20 features in 8.0 but a test for #3070 segfaults prior to 9.0 +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + unset(compiler_supports_cpp_20) +endif() -foreach(file ${files}) - get_filename_component(file_basename ${file} NAME_WE) - string(REGEX REPLACE "unit-([^$]+)" "test-\\1" testcase ${file_basename}) +############################################################################# +# test_main library with shared code to speed up build and common settings +############################################################################# - add_executable(${testcase} $ ${file}) - target_compile_definitions(${testcase} PRIVATE DOCTEST_CONFIG_SUPER_FAST_ASSERTS) - target_compile_options(${testcase} PRIVATE - $<$:/EHsc;$<$:/Od>> - $<$>:-Wno-deprecated;-Wno-float-equal> - $<$:-Wno-deprecated-declarations> - ) - target_include_directories(${testcase} PRIVATE ${CMAKE_BINARY_DIR}/include thirdparty/doctest thirdparty/fifo_map) - target_link_libraries(${testcase} PRIVATE ${NLOHMANN_JSON_TARGET_NAME}) +add_library(test_main OBJECT src/unit.cpp) +target_compile_definitions(test_main PUBLIC + DOCTEST_CONFIG_SUPER_FAST_ASSERTS + JSON_TEST_KEEP_MACROS +) +target_compile_features(test_main PRIVATE cxx_std_11) +target_compile_options(test_main PUBLIC + $<$:/EHsc;$<$:/Od>> + # MSVC: Force to always compile with W4 + # Disable warning C4566: character represented by universal-character-name '\uFF01' + # cannot be represented in the current code page (1252) + # Disable warning C4996: 'nlohmann::basic_json<...>::operator <<': was declared deprecated + $<$:/W4 /wd4566 /wd4996> + # https://github.com/nlohmann/json/issues/1114 + $<$:/bigobj> $<$:-Wa,-mbig-obj> - # add a copy with C++17 compilation - if (compiler_supports_cpp_17) - file(READ ${file} FILE_CONTENT) - string(FIND "${FILE_CONTENT}" "JSON_HAS_CPP_17" CPP_17_FOUND) - if(NOT ${CPP_17_FOUND} EQUAL -1) - add_executable(${testcase}_cpp17 $ ${file}) - target_compile_definitions(${testcase}_cpp17 PRIVATE DOCTEST_CONFIG_SUPER_FAST_ASSERTS) - target_compile_options(${testcase}_cpp17 PRIVATE - $<$:/EHsc;$<$:/Od>> - $<$>:-Wno-deprecated;-Wno-float-equal> - $<$:-Wno-deprecated-declarations> - ) - target_include_directories(${testcase}_cpp17 PRIVATE ${CMAKE_BINARY_DIR}/include thirdparty/doctest thirdparty/fifo_map) - target_link_libraries(${testcase}_cpp17 PRIVATE ${NLOHMANN_JSON_TARGET_NAME}) - target_compile_features(${testcase}_cpp17 PRIVATE cxx_std_17) + $<$>:-Wno-deprecated;-Wno-float-equal> + $<$:-Wno-deprecated-declarations> +) +target_include_directories(test_main PUBLIC + thirdparty/doctest + thirdparty/fifo_map + ${PROJECT_BINARY_DIR}/include +) +target_link_libraries(test_main PUBLIC ${NLOHMANN_JSON_TARGET_NAME}) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0 AND NOT MINGW) - # fix for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90050 - target_link_libraries(${testcase}_cpp17 PRIVATE stdc++fs) - endif() +############################################################################# +# define test- and standard-specific build settings +############################################################################# - if (JSON_FastTests) - add_test(NAME "${testcase}_cpp17" - COMMAND ${testcase}_cpp17 ${DOCTEST_TEST_FILTER} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - else() - add_test(NAME "${testcase}_cpp17" - COMMAND ${testcase}_cpp17 ${DOCTEST_TEST_FILTER} --no-skip - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - endif() - set_tests_properties("${testcase}_cpp17" PROPERTIES LABELS "all" FIXTURES_REQUIRED TEST_DATA) - endif() - endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0 + AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0 AND NOT MINGW) + # fix for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90050 + json_test_set_test_options(all CXX_STANDARDS 17 LINK_LIBRARIES stdc++fs) +endif() - if (JSON_FastTests) - add_test(NAME "${testcase}" - COMMAND ${testcase} ${DOCTEST_TEST_FILTER} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - else() - add_test(NAME "${testcase}" - COMMAND ${testcase} ${DOCTEST_TEST_FILTER} --no-skip - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - endif() - set_tests_properties("${testcase}" PROPERTIES LABELS "all" FIXTURES_REQUIRED TEST_DATA) - - if(JSON_Valgrind) - add_test(NAME "${testcase}_valgrind" - COMMAND ${memcheck_command} ${CMAKE_CURRENT_BINARY_DIR}/${testcase} ${DOCTEST_TEST_FILTER} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - ) - set_tests_properties("${testcase}_valgrind" PROPERTIES LABELS "valgrind" FIXTURES_REQUIRED TEST_DATA) - endif() -endforeach() +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # avoid stack overflow, see https://github.com/nlohmann/json/issues/2955 + json_test_set_test_options("test-cbor;test-msgpack;test-ubjson" LINK_OPTIONS /STACK:4000000) +endif() # disable exceptions for test-disabled_exceptions -target_compile_definitions(test-disabled_exceptions PUBLIC JSON_NOEXCEPTION) +json_test_set_test_options(test-disabled_exceptions COMPILE_DEFINITIONS JSON_NOEXCEPTION) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(test-disabled_exceptions PUBLIC -fno-exceptions) +json_test_set_test_options(test-disabled_exceptions COMPILE_OPTIONS -fno-exceptions) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # disabled due to https://github.com/nlohmann/json/discussions/2824 - #target_compile_options(test-disabled_exceptions PUBLIC /EH) - #target_compile_definitions(test-disabled_exceptions PUBLIC _HAS_EXCEPTIONS=0) + #json_test_set_test_options(test-disabled_exceptions COMPILE_DEFINITIONS _HAS_EXCEPTIONS=0 COMPILE_OPTIONS /EH) endif() -# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955 -if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set_property(TARGET test-cbor APPEND_STRING PROPERTY LINK_FLAGS " /STACK:4000000") - set_property(TARGET test-msgpack APPEND_STRING PROPERTY LINK_FLAGS " /STACK:4000000") - set_property(TARGET test-ubjson APPEND_STRING PROPERTY LINK_FLAGS " /STACK:4000000") +############################################################################# +# add unit tests +############################################################################# + +if("${JSON_TestStandards}" STREQUAL "") + set(test_cxx_standards 11 14 17 20 23) + unset(test_force) +else() + set(test_cxx_standards ${JSON_TestStandards}) + set(test_force FORCE) endif() +# Print selected standards marking unavailable ones with brackets +set(msg_standards "") +foreach(cxx_standard ${test_cxx_standards}) + if(compiler_supports_cpp_${cxx_standard}) + list(APPEND msg_standards ${cxx_standard}) + else() + list(APPEND msg_standards [${cxx_standard}]) + endif() +endforeach() +string(JOIN " " msg_standards ${msg_standards}) +set(msg "Testing standards: ${msg_standards}") +if(test_force) + string(APPEND msg " (forced)") +endif() +message(STATUS "${msg}") + +# *DO* use json_test_set_test_options() above this line + +file(GLOB files src/unit-*.cpp) +foreach(file ${files}) + json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}) +endforeach() + +# *DO NOT* use json_test_set_test_options() below this line + ############################################################################# # Test the generated build configs ############################################################################# diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp index 7da69dea1..bcde18b75 100644 --- a/test/src/unit-alt-string.cpp +++ b/test/src/unit-alt-string.cpp @@ -37,7 +37,7 @@ SOFTWARE. /* forward declarations */ class alt_string; -bool operator<(const char* op1, const alt_string& op2); +bool operator<(const char* op1, const alt_string& op2) noexcept; void int_to_string(alt_string& target, std::size_t value); /* @@ -152,7 +152,7 @@ class alt_string private: std::string str_impl {}; - friend bool ::operator<(const char* /*op1*/, const alt_string& /*op2*/); + friend bool ::operator<(const char* /*op1*/, const alt_string& /*op2*/) noexcept; }; void int_to_string(alt_string& target, std::size_t value) @@ -172,7 +172,7 @@ using alt_json = nlohmann::basic_json < nlohmann::adl_serializer >; -bool operator<(const char* op1, const alt_string& op2) +bool operator<(const char* op1, const alt_string& op2) noexcept { return op1 < op2.str_impl; } diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 59ff2af01..4c23e821e 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1609,7 +1609,7 @@ TEST_CASE("CBOR") // callback to set binary_seen to true if a binary value was seen bool binary_seen = false; - auto callback = [&binary_seen](int /*depth*/, json::parse_event_t /*event*/, json & parsed) + auto callback = [&binary_seen](int /*depth*/, json::parse_event_t /*event*/, json & parsed) noexcept { if (parsed.is_binary()) { diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 78fc7dd55..dcbdc7f66 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -267,7 +267,7 @@ bool accept_helper(const std::string& s) CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept(false) == !el.errored); // 5. parse with simple callback - json::parser_callback_t cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) + json::parser_callback_t cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept { return true; }; @@ -1499,7 +1499,7 @@ TEST_CASE("parser class") // test case to make sure the callback is properly evaluated after reading a key { - json::parser_callback_t cb = [](int /*unused*/, json::parse_event_t event, json& /*unused*/) + json::parser_callback_t cb = [](int /*unused*/, json::parse_event_t event, json& /*unused*/) noexcept { return event != json::parse_event_t::key; }; @@ -1538,14 +1538,14 @@ TEST_CASE("parser class") SECTION("filter nothing") { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) + json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) + json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); @@ -1555,7 +1555,7 @@ TEST_CASE("parser class") SECTION("filter everything") { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) + json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return false; }); @@ -1563,7 +1563,7 @@ TEST_CASE("parser class") // the top-level object will be discarded, leaving a null CHECK (j_object.is_null()); - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) + json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return false; }); @@ -1574,7 +1574,7 @@ TEST_CASE("parser class") SECTION("filter specific element") { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) + json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept { // filter all number(2) elements return j != json(2); @@ -1582,7 +1582,7 @@ TEST_CASE("parser class") CHECK (j_object == json({{"bar", {{"baz", 1}}}})); - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) + json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept { return j != json(2); }); @@ -1601,7 +1601,7 @@ TEST_CASE("parser class") CHECK (j_filtered1.size() == 2); CHECK (j_filtered1 == json({1, {{"qux", "baz"}}})); - json j_filtered2 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json& /*parsed*/) + json j_filtered2 = json::parse(structured_array, [](int /*unused*/, json::parse_event_t e, const json& /*parsed*/) noexcept { return e != json::parse_event_t::object_end; }); @@ -1616,7 +1616,7 @@ TEST_CASE("parser class") SECTION("first closing event") { { - json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) + json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { static bool first = true; if (e == json::parse_event_t::object_end && first) @@ -1633,7 +1633,7 @@ TEST_CASE("parser class") } { - json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) + json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { static bool first = true; if (e == json::parse_event_t::array_end && first) @@ -1657,13 +1657,13 @@ TEST_CASE("parser class") // object and array is discarded only after the closing character // has been read - json j_empty_object = json::parse("{}", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) + json j_empty_object = json::parse("{}", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { return e != json::parse_event_t::object_end; }); CHECK(j_empty_object == json()); - json j_empty_array = json::parse("[]", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) + json j_empty_array = json::parse("[]", [](int /*unused*/, json::parse_event_t e, const json& /*unused*/) noexcept { return e != json::parse_event_t::array_end; }); @@ -1731,7 +1731,7 @@ TEST_CASE("parser class") { SECTION("parser with callback") { - json::parser_callback_t cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) + json::parser_callback_t cb = [](int /*unused*/, json::parse_event_t /*unused*/, json& /*unused*/) noexcept { return true; }; diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index f4853f585..6c94add60 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -32,10 +32,6 @@ SOFTWARE. #include using nlohmann::json; -#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 -#endif - namespace { // helper function to check std::less @@ -211,7 +207,7 @@ TEST_CASE("lexicographical comparison operators") { // Skip comparing indicies 12 and 13, and 13 and 12 in C++20 pending fix // See issue #3207 -#ifdef JSON_HAS_CPP_20 +#if defined(JSON_HAS_CPP_20) || JSON_HAS_THREE_WAY_COMPARISON if ((i == 12 && j == 13) || (i == 13 && j == 12)) { continue; diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 092d5b8c3..d5cae7f29 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -41,13 +41,6 @@ using nlohmann::json; #include #include -#if (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) - #define JSON_HAS_CPP_14 -#endif - // NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") diff --git a/test/src/unit-items.cpp b/test/src/unit-items.cpp index f9021feb7..fa40f46d9 100644 --- a/test/src/unit-items.cpp +++ b/test/src/unit-items.cpp @@ -32,13 +32,6 @@ SOFTWARE. #include using nlohmann::json; -#if (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) - #define JSON_HAS_CPP_14 -#endif - // This test suite uses range for loops where values are copied. This is inefficient in usual code, but required to achieve 100% coverage. DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wrange-loop-construct") diff --git a/test/src/unit-regression1.cpp b/test/src/unit-regression1.cpp index 8991b82af..2d22b5791 100644 --- a/test/src/unit-regression1.cpp +++ b/test/src/unit-regression1.cpp @@ -42,10 +42,6 @@ using nlohmann::json; #include #include -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 -#endif - #ifdef JSON_HAS_CPP_17 #include #endif diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 8c6e41193..b8cc5107b 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -42,70 +42,8 @@ using ordered_json = nlohmann::ordered_json; #include #include -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 -#endif - #ifdef JSON_HAS_CPP_17 #include - - #if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) - #if defined(__cpp_lib_filesystem) - #define JSON_HAS_FILESYSTEM 1 - #elif defined(__cpp_lib_experimental_filesystem) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif !defined(__has_include) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #endif - - // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ - #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__) - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__clang_major__) && __clang_major__ < 7 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1940 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before iOS 13 - #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before macOS Catalina - #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - #endif -#endif - -#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_FILESYSTEM - #define JSON_HAS_FILESYSTEM 0 #endif #if JSON_HAS_EXPERIMENTAL_FILESYSTEM @@ -656,7 +594,7 @@ TEST_CASE("regression tests 2") #ifdef JSON_HAS_CPP_20 SECTION("issue #2546 - parsing containers of std::byte") { - const char DATA[] = R"("Hello, world!")"; + const char DATA[] = R"("Hello, world!")"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) const auto s = std::as_bytes(std::span(DATA)); json j = json::parse(s); CHECK(j.dump() == "\"Hello, world!\""); @@ -810,7 +748,8 @@ TEST_CASE("regression tests 2") const auto j_path = j.get(); CHECK(j_path == text_path); - CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); + // Disabled pending resolution of #3377 + // CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); } #endif diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp index 84568f818..b5657284c 100644 --- a/test/src/unit-ubjson.cpp +++ b/test/src/unit-ubjson.cpp @@ -1610,7 +1610,7 @@ TEST_CASE("UBJSON") CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&); json j; - nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) + nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); @@ -1624,7 +1624,7 @@ TEST_CASE("UBJSON") CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&); json j; - nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) + nlohmann::detail::json_sax_dom_callback_parser scp(j, [](int /*unused*/, json::parse_event_t /*unused*/, const json& /*unused*/) noexcept { return true; }); From ce35256825d8c0a21ec181333cdaad962027dcaa Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Thu, 24 Mar 2022 15:55:35 +0100 Subject: [PATCH 027/110] Refactor unit tests to use more convenient doctest assertion macros (#3393) * Add missing check * Refactor assertions in unit-algorithms.cpp * Refactor assertions in unit-bson.cpp * Refactor assertions in unit-cbor.cpp * Refactor assertions in unit-class_const_iterator.cpp * Refactor assertions in unit-class_iterator.cpp * Refactor assertions in unit-class_parser.cpp * Refactor assertions in unit-constructor1.cpp * Refactor assertions in unit-convenience.cpp * Refactor assertions in unit-conversions.cpp * Refactor assertions in unit-deserialization.cpp * Refactor assertions in unit-element_access1.cpp * Refactor assertions in unit-element_access2.cpp * Refactor assertions in unit-iterators1.cpp * Refactor assertions in unit-iterators2.cpp * Refactor assertions in unit-json_patch.cpp * Refactor assertions in unit-json_pointer.cpp * Refactor assertions in unit-modifiers.cpp * Refactor assertions in unit-msgpack.cpp * Refactor assertions in unit-reference_access.cpp * Refactor assertions in unit-regression1.cpp * Refactor assertions in unit-serialization.cpp * Refactor assertions in unit-ubjson.cpp * Refactor assertions in unit-unicode1.cpp * Apply formatting --- test/src/unit-algorithms.cpp | 4 +- test/src/unit-bson.cpp | 52 +-- test/src/unit-cbor.cpp | 140 ++----- test/src/unit-class_const_iterator.cpp | 12 +- test/src/unit-class_iterator.cpp | 12 +- test/src/unit-class_parser.cpp | 494 +++++++++---------------- test/src/unit-constructor1.cpp | 106 ++---- test/src/unit-convenience.cpp | 8 +- test/src/unit-conversions.cpp | 404 +++++++------------- test/src/unit-deserialization.cpp | 62 +--- test/src/unit-element_access1.cpp | 293 +++++---------- test/src/unit-element_access2.cpp | 351 ++++++------------ test/src/unit-iterators1.cpp | 120 ++---- test/src/unit-iterators2.cpp | 476 +++++++++--------------- test/src/unit-json_patch.cpp | 165 +++------ test/src/unit-json_pointer.cpp | 192 ++++------ test/src/unit-modifiers.cpp | 134 ++----- test/src/unit-msgpack.cpp | 116 +++--- test/src/unit-reference_access.cpp | 203 ++++------ test/src/unit-regression1.cpp | 137 ++----- test/src/unit-serialization.cpp | 12 +- test/src/unit-ubjson.cpp | 92 ++--- test/src/unit-unicode1.cpp | 28 +- 23 files changed, 1239 insertions(+), 2374 deletions(-) diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 36f02e11e..5fa860660 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -241,9 +241,7 @@ TEST_CASE("algorithms") SECTION("sorting an object") { json j({{"one", 1}, {"two", 2}}); - CHECK_THROWS_AS(std::sort(j.begin(), j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), - "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(std::sort(j.begin(), j.end()), "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } } diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 64c3607a3..1920d3155 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -44,8 +44,7 @@ TEST_CASE("BSON") SECTION("null") { json j = nullptr; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is null"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is null", json::type_error&); } SECTION("boolean") @@ -53,44 +52,38 @@ TEST_CASE("BSON") SECTION("true") { json j = true; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is boolean"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is boolean", json::type_error&); } SECTION("false") { json j = false; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is boolean"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is boolean", json::type_error&); } } SECTION("number") { json j = 42; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is number"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is number", json::type_error&); } SECTION("float") { json j = 4.2; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is number"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is number", json::type_error&); } SECTION("string") { json j = "not supported"; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is string"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is string", json::type_error&); } SECTION("array") { json j = std::vector {1, 2, 3, 4, 5, 6, 7}; - CHECK_THROWS_AS(json::to_bson(j), json::type_error&); - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is array"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.type_error.317] to serialize to BSON, top-level type must be object, but is array", json::type_error&); } } @@ -100,11 +93,10 @@ TEST_CASE("BSON") { { std::string("en\0try", 6), true } }; - CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.out_of_range.409] (/en) BSON key cannot contain code point U+0000 (at byte 2)"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.out_of_range.409] (/en) BSON key cannot contain code point U+0000 (at byte 2)", json::out_of_range&); #else - CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.out_of_range.409] BSON key cannot contain code point U+0000 (at byte 2)"); + CHECK_THROWS_WITH_AS(json::to_bson(j), "[json.exception.out_of_range.409] BSON key cannot contain code point U+0000 (at byte 2)", json::out_of_range&); #endif } @@ -119,8 +111,7 @@ TEST_CASE("BSON") 0x00, 0x00, 0x00, 0x80 }; json _; - CHECK_THROWS_AS(_ = json::from_bson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 10: syntax error while parsing BSON string: string length must be at least 1, is -2147483648"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 10: syntax error while parsing BSON string: string length must be at least 1, is -2147483648", json::parse_error&); } SECTION("objects") @@ -764,9 +755,7 @@ TEST_CASE("Incomplete BSON Input") }; json _; - CHECK_THROWS_AS(_ = json::from_bson(incomplete_bson), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_bson(incomplete_bson), - "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing BSON cstring: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(incomplete_bson), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing BSON cstring: unexpected end of input", json::parse_error&); CHECK(json::from_bson(incomplete_bson, true, false).is_discarded()); @@ -783,9 +772,7 @@ TEST_CASE("Incomplete BSON Input") }; json _; - CHECK_THROWS_AS(_ = json::from_bson(incomplete_bson), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_bson(incomplete_bson), - "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing BSON cstring: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(incomplete_bson), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing BSON cstring: unexpected end of input", json::parse_error&); CHECK(json::from_bson(incomplete_bson, true, false).is_discarded()); SaxCountdown scp(0); @@ -807,9 +794,7 @@ TEST_CASE("Incomplete BSON Input") }; json _; - CHECK_THROWS_AS(_ = json::from_bson(incomplete_bson), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_bson(incomplete_bson), - "[json.exception.parse_error.110] parse error at byte 28: syntax error while parsing BSON element list: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(incomplete_bson), "[json.exception.parse_error.110] parse error at byte 28: syntax error while parsing BSON element list: unexpected end of input", json::parse_error&); CHECK(json::from_bson(incomplete_bson, true, false).is_discarded()); SaxCountdown scp(1); @@ -824,9 +809,7 @@ TEST_CASE("Incomplete BSON Input") }; json _; - CHECK_THROWS_AS(_ = json::from_bson(incomplete_bson), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_bson(incomplete_bson), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(incomplete_bson), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BSON number: unexpected end of input", json::parse_error&); CHECK(json::from_bson(incomplete_bson, true, false).is_discarded()); SaxCountdown scp(0); @@ -872,8 +855,7 @@ TEST_CASE("Negative size of binary value") 0x00 // end marker }; json _; - CHECK_THROWS_AS(_ = json::from_bson(input), json::parse_error); - CHECK_THROWS_WITH(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1", json::parse_error); } TEST_CASE("Unsupported BSON input") @@ -887,9 +869,7 @@ TEST_CASE("Unsupported BSON input") }; json _; - CHECK_THROWS_AS(_ = json::from_bson(bson), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_bson(bson), - "[json.exception.parse_error.114] parse error at byte 5: Unsupported BSON record type 0xFF"); + CHECK_THROWS_WITH_AS(_ = json::from_bson(bson), "[json.exception.parse_error.114] parse error at byte 5: Unsupported BSON record type 0xFF", json::parse_error&); CHECK(json::from_bson(bson, true, false).is_discarded()); SaxCountdown scp(0); diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 4c23e821e..83c8ada54 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -991,17 +991,13 @@ TEST_CASE("CBOR") SECTION("no byte follows") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xf9})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xf9})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xf9})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); CHECK(json::from_cbor(std::vector({0xf9}), true, false).is_discarded()); } SECTION("only one byte follows") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xf9, 0x7c})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xf9, 0x7c})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xf9, 0x7c})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); CHECK(json::from_cbor(std::vector({0xf9, 0x7c}), true, false).is_discarded()); } } @@ -1671,97 +1667,40 @@ TEST_CASE("CBOR") SECTION("empty byte vector") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(std::vector()), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector()), - "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing CBOR value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector()), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); CHECK(json::from_cbor(std::vector(), true, false).is_discarded()); } SECTION("too short byte vector") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x18})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x19})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x19, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1a})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1a, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1a, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1a, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x62})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x62, 0x60})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x7F})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x7F, 0x60})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x82, 0x01})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x9F, 0x01})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xBF, 0x61, 0x61, 0xF5})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xA1, 0x61, 0X61})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xBF, 0x61, 0X61})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x5F})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x5F, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x41})), json::parse_error&); - - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x18})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x19})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x19, 0x00})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1a})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1a, 0x00})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1a, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1a, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing CBOR number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x62})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x62, 0x60})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x7F})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x7F, 0x60})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x82, 0x01})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x9F, 0x01})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xBF, 0x61, 0x61, 0xF5})), - "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xA1, 0x61, 0x61})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR value: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xBF, 0x61, 0x61})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR value: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x5F})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR binary: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x5F, 0x00})), - "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR binary: expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x00"); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x41})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR binary: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x18})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x19})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x19, 0x00})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1a})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1a, 0x00})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1a, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1a, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x62})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x62, 0x60})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x7F})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x7F, 0x60})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x82, 0x01})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x9F, 0x01})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xBF, 0x61, 0x61, 0xF5})), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xA1, 0x61, 0X61})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xBF, 0x61, 0X61})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x5F})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR binary: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x5F, 0x00})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR binary: expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x00", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x41})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR binary: unexpected end of input", json::parse_error&); CHECK(json::from_cbor(std::vector({0x18}), true, false).is_discarded()); CHECK(json::from_cbor(std::vector({0x19}), true, false).is_discarded()); @@ -1797,14 +1736,10 @@ TEST_CASE("CBOR") SECTION("concrete examples") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x1c})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x1c})), - "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing CBOR value: invalid byte: 0x1C"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x1c})), "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing CBOR value: invalid byte: 0x1C", json::parse_error&); CHECK(json::from_cbor(std::vector({0x1c}), true, false).is_discarded()); - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xf8})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xf8})), - "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing CBOR value: invalid byte: 0xF8"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xf8})), "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing CBOR value: invalid byte: 0xF8", json::parse_error&); CHECK(json::from_cbor(std::vector({0xf8}), true, false).is_discarded()); } @@ -1858,9 +1793,7 @@ TEST_CASE("CBOR") SECTION("invalid string in map") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xa1, 0xff, 0x01})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xa1, 0xff, 0x01})), - "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xa1, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&); CHECK(json::from_cbor(std::vector({0xa1, 0xff, 0x01}), true, false).is_discarded()); } @@ -1877,9 +1810,7 @@ TEST_CASE("CBOR") SECTION("strict mode") { json _; - CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR value: expected end of input; last byte: 0xF6"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR value: expected end of input; last byte: 0xF6", json::parse_error&); CHECK(json::from_cbor(vec, true, false).is_discarded()); } } @@ -2746,8 +2677,7 @@ TEST_CASE("Tagged values") // parse error when parsing tagged value json _; - CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error); - CHECK_THROWS_WITH(_ = json::from_cbor(vec), "[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: invalid byte: 0xD8"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: invalid byte: 0xD8", json::parse_error); // binary without subtype when tags are ignored json jb = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::ignore); diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 33ec85768..57e8e539d 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -148,8 +148,7 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(*it, json::invalid_iterator&); - CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("number") @@ -158,8 +157,7 @@ TEST_CASE("const_iterator class") json::const_iterator it = j.cbegin(); CHECK(*it == json(17)); it = j.cend(); - CHECK_THROWS_AS(*it, json::invalid_iterator&); - CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("object") @@ -183,8 +181,7 @@ TEST_CASE("const_iterator class") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); - CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("number") @@ -193,8 +190,7 @@ TEST_CASE("const_iterator class") json::const_iterator it = j.cbegin(); CHECK(std::string(it->type_name()) == "number"); it = j.cend(); - CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); - CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("object") diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index e3a972f97..64215f3e9 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -138,8 +138,7 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK_THROWS_AS(*it, json::invalid_iterator&); - CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("number") @@ -148,8 +147,7 @@ TEST_CASE("iterator class") json::iterator it = j.begin(); CHECK(*it == json(17)); it = j.end(); - CHECK_THROWS_AS(*it, json::invalid_iterator&); - CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("object") @@ -173,8 +171,7 @@ TEST_CASE("iterator class") { json j(json::value_t::null); json::iterator it = j.begin(); - CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); - CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("number") @@ -183,8 +180,7 @@ TEST_CASE("iterator class") json::iterator it = j.begin(); CHECK(std::string(it->type_name()) == "number"); it = j.end(); - CHECK_THROWS_AS(std::string(it->type_name()), json::invalid_iterator&); - CHECK_THROWS_WITH(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } SECTION("object") diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index dcbdc7f66..95a958d1f 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -377,88 +377,48 @@ TEST_CASE("parser class") SECTION("errors") { // error: tab in string - CHECK_THROWS_AS(parser_helper("\"\t\""), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("\"\t\""), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"'"); + CHECK_THROWS_WITH_AS(parser_helper("\"\t\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"'", json::parse_error&); // error: newline in string - CHECK_THROWS_AS(parser_helper("\"\n\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\r\""), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("\"\n\""), - "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\r\""), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"'"); + CHECK_THROWS_WITH_AS(parser_helper("\"\n\""), "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\r\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"'", json::parse_error&); // error: backspace in string - CHECK_THROWS_AS(parser_helper("\"\b\""), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("\"\b\""), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"'"); + CHECK_THROWS_WITH_AS(parser_helper("\"\b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"'", json::parse_error&); // improve code coverage CHECK_THROWS_AS(parser_helper("\uFF01"), json::parse_error&); CHECK_THROWS_AS(parser_helper("[-4:1,]"), json::parse_error&); // unescaped control characters - CHECK_THROWS_AS(parser_helper("\"\x00\""), json::parse_error&); // NOLINT(bugprone-string-literal-with-embedded-nul) - CHECK_THROWS_AS(parser_helper("\"\x01\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x02\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x03\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x04\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x05\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x06\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x07\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x08\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x09\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x0a\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x0b\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x0c\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x0d\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x0e\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x0f\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x10\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x11\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x12\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x13\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x14\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x15\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x16\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x17\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x18\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x19\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x1a\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x1b\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x1c\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x1d\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x1e\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\x1f\""), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("\"\x00\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'"); // NOLINT(bugprone-string-literal-with-embedded-nul) - CHECK_THROWS_WITH(parser_helper("\"\x01\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0001 (SOH) must be escaped to \\u0001; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x02\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0002 (STX) must be escaped to \\u0002; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x03\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0003 (ETX) must be escaped to \\u0003; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x04\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0004 (EOT) must be escaped to \\u0004; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x05\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0005 (ENQ) must be escaped to \\u0005; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x06\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0006 (ACK) must be escaped to \\u0006; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x07\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0007 (BEL) must be escaped to \\u0007; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x08\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x09\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x0a\""), "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x0b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000B (VT) must be escaped to \\u000B; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x0c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x0d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x0e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000E (SO) must be escaped to \\u000E; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x0f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000F (SI) must be escaped to \\u000F; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x10\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0010 (DLE) must be escaped to \\u0010; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x11\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0011 (DC1) must be escaped to \\u0011; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x12\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0012 (DC2) must be escaped to \\u0012; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x13\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0013 (DC3) must be escaped to \\u0013; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x14\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0014 (DC4) must be escaped to \\u0014; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x15\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0015 (NAK) must be escaped to \\u0015; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x16\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0016 (SYN) must be escaped to \\u0016; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x17\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0017 (ETB) must be escaped to \\u0017; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x18\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0018 (CAN) must be escaped to \\u0018; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x19\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0019 (EM) must be escaped to \\u0019; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x1a\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001A (SUB) must be escaped to \\u001A; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x1b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001B (ESC) must be escaped to \\u001B; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x1c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001C (FS) must be escaped to \\u001C; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x1d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001D (GS) must be escaped to \\u001D; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x1e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001E (RS) must be escaped to \\u001E; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\x1f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001F (US) must be escaped to \\u001F; last read: '\"'"); + CHECK_THROWS_WITH_AS(parser_helper("\"\x00\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'", json::parse_error&); // NOLINT(bugprone-string-literal-with-embedded-nul) + CHECK_THROWS_WITH_AS(parser_helper("\"\x01\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0001 (SOH) must be escaped to \\u0001; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x02\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0002 (STX) must be escaped to \\u0002; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x03\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0003 (ETX) must be escaped to \\u0003; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x04\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0004 (EOT) must be escaped to \\u0004; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x05\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0005 (ENQ) must be escaped to \\u0005; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x06\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0006 (ACK) must be escaped to \\u0006; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x07\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0007 (BEL) must be escaped to \\u0007; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x08\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x09\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0a\""), "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000B (VT) must be escaped to \\u000B; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000E (SO) must be escaped to \\u000E; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x0f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+000F (SI) must be escaped to \\u000F; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x10\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0010 (DLE) must be escaped to \\u0010; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x11\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0011 (DC1) must be escaped to \\u0011; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x12\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0012 (DC2) must be escaped to \\u0012; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x13\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0013 (DC3) must be escaped to \\u0013; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x14\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0014 (DC4) must be escaped to \\u0014; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x15\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0015 (NAK) must be escaped to \\u0015; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x16\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0016 (SYN) must be escaped to \\u0016; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x17\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0017 (ETB) must be escaped to \\u0017; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x18\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0018 (CAN) must be escaped to \\u0018; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x19\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0019 (EM) must be escaped to \\u0019; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1a\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001A (SUB) must be escaped to \\u001A; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1b\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001B (ESC) must be escaped to \\u001B; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1c\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001C (FS) must be escaped to \\u001C; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1d\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001D (GS) must be escaped to \\u001D; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1e\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001E (RS) must be escaped to \\u001E; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\x1f\""), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+001F (US) must be escaped to \\u001F; last read: '\"'", json::parse_error&); SECTION("additional test for null byte") { @@ -469,8 +429,7 @@ TEST_CASE("parser class") std::string s = "\"1\""; s[1] = '\0'; json _; - CHECK_THROWS_AS(_ = json::parse(s.begin(), s.end()), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(s.begin(), s.end()), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0000 (NUL) must be escaped to \\u0000; last read: '\"'"); + CHECK_THROWS_WITH_AS(_ = json::parse(s.begin(), s.end()), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: control character U+0000 (NUL) must be escaped to \\u0000; last read: '\"'", json::parse_error&); } } @@ -641,68 +600,49 @@ TEST_CASE("parser class") SECTION("overflow") { // overflows during parsing yield an exception - CHECK_THROWS_AS(parser_helper("1.18973e+4932").empty(), json::out_of_range&); - CHECK_THROWS_WITH(parser_helper("1.18973e+4932").empty(), - "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'"); + CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } SECTION("invalid numbers") { - CHECK_THROWS_AS(parser_helper("01"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("--1"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1E"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1E-"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1.E1"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-1E"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0E#"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0E-#"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0#"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0.0:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0.0Z"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0E123:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0e0-:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0e-:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0f"), json::parse_error&); - // numbers must not begin with "+" CHECK_THROWS_AS(parser_helper("+1"), json::parse_error&); CHECK_THROWS_AS(parser_helper("+0"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("01"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected number literal; expected end of input"); - CHECK_THROWS_WITH(parser_helper("-01"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - unexpected number literal; expected end of input"); - CHECK_THROWS_WITH(parser_helper("--1"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'"); - CHECK_THROWS_WITH(parser_helper("1."), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.'"); - CHECK_THROWS_WITH(parser_helper("1E"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E'"); - CHECK_THROWS_WITH(parser_helper("1E-"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '1E-'"); - CHECK_THROWS_WITH(parser_helper("1.E1"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.E'"); - CHECK_THROWS_WITH(parser_helper("-1E"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-1E'"); - CHECK_THROWS_WITH(parser_helper("-0E#"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-0E#'"); - CHECK_THROWS_WITH(parser_helper("-0E-#"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0E-#'"); - CHECK_THROWS_WITH(parser_helper("-0#"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: '-0#'; expected end of input"); - CHECK_THROWS_WITH(parser_helper("-0.0:"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ':'; expected end of input"); - CHECK_THROWS_WITH(parser_helper("-0.0Z"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: '-0.0Z'; expected end of input"); - CHECK_THROWS_WITH(parser_helper("-0E123:"), - "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - unexpected ':'; expected end of input"); - CHECK_THROWS_WITH(parser_helper("-0e0-:"), - "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'; expected end of input"); - CHECK_THROWS_WITH(parser_helper("-0e-:"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0e-:'"); - CHECK_THROWS_WITH(parser_helper("-0f"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: '-0f'; expected end of input"); + CHECK_THROWS_WITH_AS(parser_helper("01"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected number literal; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-01"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - unexpected number literal; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("--1"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E-"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '1E-'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1.E1"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.E'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-1E"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-1E'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0E#"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '-0E#'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0E-#"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0E-#'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0#"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: '-0#'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0.0:"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - unexpected ':'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0.0Z"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: '-0.0Z'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0E123:"), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - unexpected ':'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0e0-:"), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'; expected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0e-:"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid number; expected digit after exponent sign; last read: '-0e-:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0f"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: '-0f'; expected end of input", json::parse_error&); } } } @@ -972,171 +912,120 @@ TEST_CASE("parser class") SECTION("parse errors") { // unexpected end of number - CHECK_THROWS_AS(parser_helper("0."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("--"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-0."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("-:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("0.:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("e."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1e."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1e/"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1e:"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1E."), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1E/"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("1E:"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("0."), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.'"); - CHECK_THROWS_WITH(parser_helper("-"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-'"); - CHECK_THROWS_WITH(parser_helper("--"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'"); - CHECK_THROWS_WITH(parser_helper("-0."), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after '.'; last read: '-0.'"); - CHECK_THROWS_WITH(parser_helper("-."), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-.'"); - CHECK_THROWS_WITH(parser_helper("-:"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'"); - CHECK_THROWS_WITH(parser_helper("0.:"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.:'"); - CHECK_THROWS_WITH(parser_helper("e."), - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'e'"); - CHECK_THROWS_WITH(parser_helper("1e."), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e.'"); - CHECK_THROWS_WITH(parser_helper("1e/"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e/'"); - CHECK_THROWS_WITH(parser_helper("1e:"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e:'"); - CHECK_THROWS_WITH(parser_helper("1E."), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E.'"); - CHECK_THROWS_WITH(parser_helper("1E/"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E/'"); - CHECK_THROWS_WITH(parser_helper("1E:"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E:'"); + CHECK_THROWS_WITH_AS(parser_helper("0."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("--"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '--'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-0."), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid number; expected digit after '.'; last read: '-0.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-."), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("-:"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid number; expected digit after '-'; last read: '-:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("0.:"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected digit after '.'; last read: '0.:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("e."), + "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'e'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1e."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1e/"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e/'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1e:"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e:'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E."), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E.'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E/"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E/'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("1E:"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1E:'", json::parse_error&); // unexpected end of null - CHECK_THROWS_AS(parser_helper("n"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("nu"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("nul"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("nulk"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("nulm"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("n"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'n'"); - CHECK_THROWS_WITH(parser_helper("nu"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'nu'"); - CHECK_THROWS_WITH(parser_helper("nul"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nul'"); - CHECK_THROWS_WITH(parser_helper("nulk"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulk'"); - CHECK_THROWS_WITH(parser_helper("nulm"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulm'"); + CHECK_THROWS_WITH_AS(parser_helper("n"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'n'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nu"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'nu'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nul"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nul'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nulk"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulk'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("nulm"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'nulm'", json::parse_error&); // unexpected end of true - CHECK_THROWS_AS(parser_helper("t"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("tr"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("tru"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("trud"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("truf"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("t"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 't'"); - CHECK_THROWS_WITH(parser_helper("tr"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'tr'"); - CHECK_THROWS_WITH(parser_helper("tru"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'tru'"); - CHECK_THROWS_WITH(parser_helper("trud"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'trud'"); - CHECK_THROWS_WITH(parser_helper("truf"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'truf'"); + CHECK_THROWS_WITH_AS(parser_helper("t"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 't'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("tr"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'tr'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("tru"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'tru'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("trud"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'trud'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("truf"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'truf'", json::parse_error&); // unexpected end of false - CHECK_THROWS_AS(parser_helper("f"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("fa"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("fal"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("fals"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("falsd"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("falsf"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("f"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'f'"); - CHECK_THROWS_WITH(parser_helper("fa"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'fa'"); - CHECK_THROWS_WITH(parser_helper("fal"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'fal'"); - CHECK_THROWS_WITH(parser_helper("fals"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'fals'"); - CHECK_THROWS_WITH(parser_helper("falsd"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsd'"); - CHECK_THROWS_WITH(parser_helper("falsf"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsf'"); + CHECK_THROWS_WITH_AS(parser_helper("f"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'f'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("fa"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid literal; last read: 'fa'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("fal"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid literal; last read: 'fal'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("fals"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'fals'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("falsd"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsd'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("falsf"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: 'falsf'", json::parse_error&); // missing/unexpected end of array - CHECK_THROWS_AS(parser_helper("["), json::parse_error&); - CHECK_THROWS_AS(parser_helper("[1"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("[1,"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("[1,]"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("]"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("["), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); - CHECK_THROWS_WITH(parser_helper("[1"), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing array - unexpected end of input; expected ']'"); - CHECK_THROWS_WITH(parser_helper("[1,"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); - CHECK_THROWS_WITH(parser_helper("[1,]"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal"); - CHECK_THROWS_WITH(parser_helper("]"), - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(parser_helper("["), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("[1"), + "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("[1,"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("[1,]"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("]"), + "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal", json::parse_error&); // missing/unexpected end of object - CHECK_THROWS_AS(parser_helper("{"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("{\"foo\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("{\"foo\":"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("{\"foo\":}"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("{\"foo\":1,}"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("}"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("{"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected end of input; expected string literal"); - CHECK_THROWS_WITH(parser_helper("{\"foo\""), - "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing object separator - unexpected end of input; expected ':'"); - CHECK_THROWS_WITH(parser_helper("{\"foo\":"), - "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); - CHECK_THROWS_WITH(parser_helper("{\"foo\":}"), - "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal"); - CHECK_THROWS_WITH(parser_helper("{\"foo\":1,}"), - "[json.exception.parse_error.101] parse error at line 1, column 10: syntax error while parsing object key - unexpected '}'; expected string literal"); - CHECK_THROWS_WITH(parser_helper("}"), - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(parser_helper("{"), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected end of input; expected string literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\""), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing object separator - unexpected end of input; expected ':'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\":"), + "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\":}"), + "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("{\"foo\":1,}"), + "[json.exception.parse_error.101] parse error at line 1, column 10: syntax error while parsing object key - unexpected '}'; expected string literal", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("}"), + "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected '}'; expected '[', '{', or a literal", json::parse_error&); // missing/unexpected end of string - CHECK_THROWS_AS(parser_helper("\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u0\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u01\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u012\""), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u0"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u01"), json::parse_error&); - CHECK_THROWS_AS(parser_helper("\"\\u012"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("\""), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'"); - CHECK_THROWS_WITH(parser_helper("\"\\\""), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: missing closing quote; last read: '\"\\\"'"); - CHECK_THROWS_WITH(parser_helper("\"\\u\""), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u\"'"); - CHECK_THROWS_WITH(parser_helper("\"\\u0\""), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0\"'"); - CHECK_THROWS_WITH(parser_helper("\"\\u01\""), - "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01\"'"); - CHECK_THROWS_WITH(parser_helper("\"\\u012\""), - "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012\"'"); - CHECK_THROWS_WITH(parser_helper("\"\\u"), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u'"); - CHECK_THROWS_WITH(parser_helper("\"\\u0"), - "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0'"); - CHECK_THROWS_WITH(parser_helper("\"\\u01"), - "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01'"); - CHECK_THROWS_WITH(parser_helper("\"\\u012"), - "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012'"); + CHECK_THROWS_WITH_AS(parser_helper("\""), + "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: missing closing quote; last read: '\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\\""), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: missing closing quote; last read: '\"\\\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u\""), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u0\""), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u01\""), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u012\""), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012\"'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u"), + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u0"), + "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u0'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u01"), + "[json.exception.parse_error.101] parse error at line 1, column 6: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u01'", json::parse_error&); + CHECK_THROWS_WITH_AS(parser_helper("\"\\u012"), + "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\u012'", json::parse_error&); // invalid escapes for (int c = 1; c < 128; ++c) @@ -1285,19 +1174,14 @@ TEST_CASE("parser class") json _; // missing part of a surrogate pair - CHECK_THROWS_AS(_ = json::parse("\"\\uD80C\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD80C\""), - "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\"'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\""), "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\"'", json::parse_error&); // invalid surrogate pair - CHECK_THROWS_AS(_ = json::parse("\"\\uD80C\\uD80C\""), json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD80C\\u0000\""), json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD80C\\uFFFF\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD80C\\uD80C\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uD80C'"); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD80C\\u0000\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\u0000'"); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD80C\\uFFFF\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uFFFF'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\\uD80C\""), + "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uD80C'", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\\u0000\""), + "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\u0000'", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD80C\\uFFFF\""), + "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD80C\\uFFFF'", json::parse_error&); } SECTION("parse errors (accept)") @@ -1489,13 +1373,9 @@ TEST_CASE("parser class") SECTION("tests found by mutate++") { // test case to make sure no comma precedes the first key - CHECK_THROWS_AS(parser_helper("{,\"key\": false}"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("{,\"key\": false}"), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected ','; expected string literal"); + CHECK_THROWS_WITH_AS(parser_helper("{,\"key\": false}"), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected ','; expected string literal", json::parse_error&); // test case to make sure an object is properly closed - CHECK_THROWS_AS(parser_helper("[{\"key\": false true]"), json::parse_error&); - CHECK_THROWS_WITH(parser_helper("[{\"key\": false true]"), - "[json.exception.parse_error.101] parse error at line 1, column 19: syntax error while parsing object - unexpected true literal; expected '}'"); + CHECK_THROWS_WITH_AS(parser_helper("[{\"key\": false true]"), "[json.exception.parse_error.101] parse error at line 1, column 19: syntax error while parsing object - unexpected true literal; expected '}'", json::parse_error&); // test case to make sure the callback is properly evaluated after reading a key { @@ -1739,13 +1619,9 @@ TEST_CASE("parser class") CHECK(json::parse("{\"foo\": true:", cb, false).is_discarded()); json _; - CHECK_THROWS_AS(_ = json::parse("{\"foo\": true:", cb), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("{\"foo\": true:", cb), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing object - unexpected ':'; expected '}'"); + CHECK_THROWS_WITH_AS(_ = json::parse("{\"foo\": true:", cb), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing object - unexpected ':'; expected '}'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("1.18973e+4932", cb), json::out_of_range&); - CHECK_THROWS_WITH(_ = json::parse("1.18973e+4932", cb), - "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'"); + CHECK_THROWS_WITH_AS(_ = json::parse("1.18973e+4932", cb), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } SECTION("SAX parser") diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 03ad689ab..195521226 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -305,12 +305,9 @@ TEST_CASE("constructors") { json j{1}; - CHECK_THROWS_AS((j.get>()), json::out_of_range&); - CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); - CHECK_THROWS_AS((j.get>()), json::out_of_range&); - CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); - CHECK_THROWS_AS((j.get>()), json::out_of_range&); - CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); + CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range", json::out_of_range&); } SECTION("std::forward_list") @@ -1091,10 +1088,7 @@ TEST_CASE("constructors") SECTION("object with error") { json _; - CHECK_THROWS_AS(_ = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - json::type_error&); - CHECK_THROWS_WITH(_ = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - "[json.exception.type_error.301] cannot create object from initializer list"); + CHECK_THROWS_WITH_AS(_ = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), "[json.exception.type_error.301] cannot create object from initializer list", json::type_error&); } SECTION("empty array") @@ -1340,18 +1334,14 @@ TEST_CASE("constructors") { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH_AS(json(jobject.begin(), jobject2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(jobject2.begin(), jobject.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH_AS(json(jobject.cbegin(), jobject2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(jobject2.cbegin(), jobject.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); } } } @@ -1405,18 +1395,14 @@ TEST_CASE("constructors") { json jarray = {1, 2, 3, 4}; json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH_AS(json(jarray.begin(), jarray2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(jarray2.begin(), jarray.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); } { json jarray = {1, 2, 3, 4}; json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible"); + CHECK_THROWS_WITH_AS(json(jarray.cbegin(), jarray2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(jarray2.cbegin(), jarray.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator&); } } } @@ -1429,15 +1415,11 @@ TEST_CASE("constructors") { { json j; - CHECK_THROWS_AS(json(j.begin(), j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.begin(), j.end()), - "[json.exception.invalid_iterator.206] cannot construct with iterators from null"); + CHECK_THROWS_WITH_AS(json(j.begin(), j.end()), "[json.exception.invalid_iterator.206] cannot construct with iterators from null", json::invalid_iterator&); } { json j; - CHECK_THROWS_AS(json(j.cbegin(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), - "[json.exception.invalid_iterator.206] cannot construct with iterators from null"); + CHECK_THROWS_WITH_AS(json(j.cbegin(), j.cend()), "[json.exception.invalid_iterator.206] cannot construct with iterators from null", json::invalid_iterator&); } } @@ -1532,17 +1514,13 @@ TEST_CASE("constructors") { { json j = "foo"; - CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = "bar"; - CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -1550,17 +1528,13 @@ TEST_CASE("constructors") { { json j = false; - CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = true; - CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -1568,17 +1542,13 @@ TEST_CASE("constructors") { { json j = 17; - CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = 17; - CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -1586,17 +1556,13 @@ TEST_CASE("constructors") { { json j = 17u; - CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = 17u; - CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -1604,17 +1570,13 @@ TEST_CASE("constructors") { { json j = 23.42; - CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = 23.42; - CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } } diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index e66b42cac..f2018bf3c 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -106,12 +106,8 @@ TEST_CASE("convenience functions") check_escaped("\x1f", "\\u001f"); // invalid UTF-8 characters - CHECK_THROWS_AS(check_escaped("ä\xA9ü"), json::type_error&); - CHECK_THROWS_WITH(check_escaped("ä\xA9ü"), - "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9"); + CHECK_THROWS_WITH_AS(check_escaped("ä\xA9ü"), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); - CHECK_THROWS_AS(check_escaped("\xC2"), json::type_error&); - CHECK_THROWS_WITH(check_escaped("\xC2"), - "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2"); + CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); } } diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index d5cae7f29..c61b249c2 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -94,43 +94,27 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-object type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::string).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), - json::type_error&); - CHECK_THROWS_AS( - json(json::value_t::number_unsigned).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), - json::type_error&); - - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be object, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be object, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be object, but is array"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be object, but is array", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::string).get(), - "[json.exception.type_error.302] type must be object, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "[json.exception.type_error.302] type must be object, " - "but is boolean"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be object, but is string", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get(), + "[json.exception.type_error.302] type must be object, " + "but is boolean", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_integer).get(), - "[json.exception.type_error.302] type must be object, but is number"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be object, but is number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_unsigned).get(), - "[json.exception.type_error.302] type must be object, but is number"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be object, but is number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_float).get(), - "[json.exception.type_error.302] type must be object, but is number"); + "[json.exception.type_error.302] type must be object, but is number", json::type_error&); } } @@ -248,11 +232,9 @@ TEST_CASE("value conversion") std::forward_list a = j.get>(); CHECK(json(a) == j); - CHECK_THROWS_AS(json(json::value_t::null).get>(), - json::type_error&); - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get>(), - "[json.exception.type_error.302] type must be array, but is null"); + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } SECTION("std::vector") @@ -260,11 +242,9 @@ TEST_CASE("value conversion") std::vector a = j.get>(); CHECK(json(a) == j); - CHECK_THROWS_AS(json(json::value_t::null).get>(), - json::type_error&); - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get>(), - "[json.exception.type_error.302] type must be array, but is null"); + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); #if !defined(JSON_NOEXCEPTION) SECTION("reserve is called on containers that supports it") @@ -299,45 +279,30 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-array type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::string).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), - json::type_error&); - - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::object).get>(), - "[json.exception.type_error.302] type must be array, but is object"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is object", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be array, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be array, but is object"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is object", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::string).get(), - "[json.exception.type_error.302] type must be array, but is string"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is string", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::boolean).get(), - "[json.exception.type_error.302] type must be array, but is boolean"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is boolean", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_integer).get(), - "[json.exception.type_error.302] type must be array, but is number"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_unsigned).get(), - "[json.exception.type_error.302] type must be array, but is number"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_float).get(), - "[json.exception.type_error.302] type must be array, but is number"); + "[json.exception.type_error.302] type must be array, but is number", json::type_error&); } } @@ -465,70 +430,46 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-string type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), - json::type_error&); - CHECK_THROWS_AS( - json(json::value_t::number_unsigned).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), - json::type_error&); - - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be string, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be string, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be string, but is object"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be string, but is object", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be string, but is array"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "[json.exception.type_error.302] type must be string, " - "but is boolean"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be string, but is array", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get(), + "[json.exception.type_error.302] type must be string, " + "but is boolean", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_integer).get(), - "[json.exception.type_error.302] type must be string, but is number"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be string, but is number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_unsigned).get(), - "[json.exception.type_error.302] type must be string, but is number"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be string, but is number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_float).get(), - "[json.exception.type_error.302] type must be string, but is number"); + "[json.exception.type_error.302] type must be string, but is number", json::type_error&); } #if defined(JSON_HAS_CPP_17) SECTION("exception in case of a non-string type using string_view") { - CHECK_THROWS_AS(json(json::value_t::null).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), json::type_error&); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be string, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be string, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be string, but is array"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "[json.exception.type_error.302] type must be string, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "[json.exception.type_error.302] type must be string, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "[json.exception.type_error.302] type must be string, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "[json.exception.type_error.302] type must be string, but is number"); + CHECK_THROWS_WITH_AS(json(json::value_t::null).get(), + "[json.exception.type_error.302] type must be string, but is null", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::object).get(), + "[json.exception.type_error.302] type must be string, but is object", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::array).get(), + "[json.exception.type_error.302] type must be string, but is array", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get(), + "[json.exception.type_error.302] type must be string, but is boolean", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::number_integer).get(), + "[json.exception.type_error.302] type must be string, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::number_unsigned).get(), + "[json.exception.type_error.302] type must be string, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::number_float).get(), + "[json.exception.type_error.302] type must be string, but is number", json::type_error&); } #endif } @@ -570,29 +511,20 @@ TEST_CASE("value conversion") auto n2 = j.get(); CHECK(n2 == n); - CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), json::type_error&); - - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "[json.exception.type_error.302] type must be null, but is string"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be null, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be null, but is array"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "[json.exception.type_error.302] type must be null, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "[json.exception.type_error.302] type must be null, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "[json.exception.type_error.302] type must be null, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "[json.exception.type_error.302] type must be null, but is number"); - + CHECK_THROWS_WITH_AS(json(json::value_t::string).get(), + "[json.exception.type_error.302] type must be null, but is string", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::object).get(), + "[json.exception.type_error.302] type must be null, but is object", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::array).get(), + "[json.exception.type_error.302] type must be null, but is array", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get(), + "[json.exception.type_error.302] type must be null, but is boolean", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::number_integer).get(), + "[json.exception.type_error.302] type must be null, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::number_unsigned).get(), + "[json.exception.type_error.302] type must be null, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::number_float).get(), + "[json.exception.type_error.302] type must be null, but is number", json::type_error&); } #if JSON_USE_IMPLICIT_CONVERSIONS @@ -648,49 +580,33 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-number type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::string).get(), - json::type_error&); CHECK_THROWS_AS(json(json::value_t::string).get(), json::type_error&); - CHECK_THROWS_AS( - json(json::value_t::number_integer).get(), - json::type_error&); - CHECK_THROWS_AS( - json(json::value_t::number_unsigned).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), - json::type_error&); - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be boolean, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be boolean, " - "but is object"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be boolean, but is null", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::object).get(), + "[json.exception.type_error.302] type must be boolean, " + "but is object", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be boolean, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "[json.exception.type_error.302] type must be boolean, " - "but is string"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be boolean, but is array", json::type_error&); + CHECK_THROWS_WITH_AS(json(json::value_t::string).get(), + "[json.exception.type_error.302] type must be boolean, " + "but is string", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_integer).get(), "[json.exception.type_error.302] type must be boolean, but is " - "number"); - CHECK_THROWS_WITH( + "number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_unsigned).get(), "[json.exception.type_error.302] type must be boolean, but is " - "number"); - CHECK_THROWS_WITH( + "number", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::number_float).get(), "[json.exception.type_error.302] type must be boolean, but is " - "number"); + "number", json::type_error&); } } @@ -927,34 +843,22 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-number type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::string).get(), - json::type_error&); - CHECK_THROWS_AS( - json(json::value_t::boolean).get(), - json::type_error&); - - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be number, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be number, but is object"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is object", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be number, but is array"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is array", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::string).get(), - "[json.exception.type_error.302] type must be number, but is string"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is string", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::boolean).get(), "[json.exception.type_error.302] type must be number, but is " - "boolean"); + "boolean", json::type_error&); CHECK_NOTHROW( json(json::value_t::number_float).get()); @@ -1202,33 +1106,22 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-string type") { - CHECK_THROWS_AS(json(json::value_t::null).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::object).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::array).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::string).get(), - json::type_error&); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), - json::type_error&); - - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( json(json::value_t::null).get(), - "[json.exception.type_error.302] type must be number, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::object).get(), - "[json.exception.type_error.302] type must be number, but is object"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is object", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::array).get(), - "[json.exception.type_error.302] type must be number, but is array"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is array", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::string).get(), - "[json.exception.type_error.302] type must be number, but is string"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be number, but is string", json::type_error&); + CHECK_THROWS_WITH_AS( json(json::value_t::boolean).get(), "[json.exception.type_error.302] type must be number, but is " - "boolean"); + "boolean", json::type_error&); CHECK_NOTHROW( json(json::value_t::number_integer).get()); @@ -1443,11 +1336,9 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-object type") { - CHECK_THROWS_AS((json().get>()), - json::type_error&); - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be object, but is null"); + "[json.exception.type_error.302] type must be object, but is null", json::type_error&); } } @@ -1488,9 +1379,8 @@ TEST_CASE("value conversion") SECTION("std::array is larger than JSON") { std::array arr6 = {{1, 2, 3, 4, 5, 6}}; - CHECK_THROWS_AS(j1.get_to(arr6), json::out_of_range&); - CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.out_of_range.401] " - "array index 4 is out of range"); + CHECK_THROWS_WITH_AS(j1.get_to(arr6), "[json.exception.out_of_range.401] " + "array index 4 is out of range", json::out_of_range&); } SECTION("std::array is smaller than JSON") @@ -1557,14 +1447,12 @@ TEST_CASE("value conversion") json j7 = {0, 1, 2, 3}; json j8 = 2; - CHECK_THROWS_AS((j7.get>()), json::type_error&); - CHECK_THROWS_AS((j8.get>()), json::type_error&); - CHECK_THROWS_WITH((j7.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number"); - CHECK_THROWS_WITH((j8.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number"); + CHECK_THROWS_WITH_AS((j7.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); + CHECK_THROWS_WITH_AS((j8.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); SECTION("superfluous entries") { @@ -1584,14 +1472,12 @@ TEST_CASE("value conversion") json j7 = {0, 1, 2, 3}; json j8 = 2; - CHECK_THROWS_AS((j7.get>()), json::type_error&); - CHECK_THROWS_AS((j8.get>()), json::type_error&); - CHECK_THROWS_WITH((j7.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number"); - CHECK_THROWS_WITH((j8.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number"); + CHECK_THROWS_WITH_AS((j7.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); + CHECK_THROWS_WITH_AS((j8.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); SECTION("superfluous entries") { @@ -1603,32 +1489,26 @@ TEST_CASE("value conversion") SECTION("exception in case of a non-object type") { - CHECK_THROWS_AS((json().get>()), json::type_error&); - CHECK_THROWS_AS((json().get>()), json::type_error&); - CHECK_THROWS_AS((json().get>()), json::type_error&); - CHECK_THROWS_AS((json().get>()), json::type_error&); - CHECK_THROWS_AS((json().get>()), json::type_error&); - // does type really must be an array? or it rather must not be null? // that's what I thought when other test like this one broke - CHECK_THROWS_WITH( + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be array, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be array, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be array, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be array, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be array, but is null"); - CHECK_THROWS_WITH( + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( (json().get>()), - "[json.exception.type_error.302] type must be array, but is null"); + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } } } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 45a8c0e6b..ebf25ac1b 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -281,20 +281,16 @@ TEST_CASE("deserialization") SECTION("stream") { std::stringstream ss1; - std::stringstream ss2; std::stringstream ss3; std::stringstream ss4; std::stringstream ss5; ss1 << R"(["foo",1,2,3,false,{"one":1})"; - ss2 << R"(["foo",1,2,3,false,{"one":1})"; ss3 << R"(["foo",1,2,3,false,{"one":1})"; ss4 << R"(["foo",1,2,3,false,{"one":1})"; ss5 << R"(["foo",1,2,3,false,{"one":1})"; json _; - CHECK_THROWS_AS(_ = json::parse(ss1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(ss2), - "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH_AS(_ = json::parse(ss1), "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); CHECK(!json::accept(ss3)); json j_error; @@ -317,9 +313,7 @@ TEST_CASE("deserialization") { json::string_t s = R"(["foo",1,2,3,false,{"one":1})"; json _; - CHECK_THROWS_AS(_ = json::parse(s), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(s), - "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH_AS(_ = json::parse(s), "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); CHECK(!json::accept(s)); json j_error; @@ -340,33 +334,23 @@ TEST_CASE("deserialization") SECTION("operator<<") { - std::stringstream ss1; - std::stringstream ss2; - ss1 << R"(["foo",1,2,3,false,{"one":1})"; - ss2 << R"(["foo",1,2,3,false,{"one":1})"; + std::stringstream ss; + ss << R"(["foo",1,2,3,false,{"one":1})"; json j; - CHECK_THROWS_AS(j << ss1, json::parse_error&); - CHECK_THROWS_WITH(j << ss2, - "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH_AS(j << ss, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); } SECTION("operator>>") { - std::stringstream ss1; - std::stringstream ss2; - ss1 << R"(["foo",1,2,3,false,{"one":1})"; - ss2 << R"(["foo",1,2,3,false,{"one":1})"; + std::stringstream ss; + ss << R"(["foo",1,2,3,false,{"one":1})"; json j; - CHECK_THROWS_AS(ss1 >> j, json::parse_error&); - CHECK_THROWS_WITH(ss2 >> j, - "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); } SECTION("user-defined string literal") { - CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, json::parse_error&); - CHECK_THROWS_WITH("[\"foo\",1,2,3,false,{\"one\":1}"_json, - "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&); } } @@ -648,9 +632,7 @@ TEST_CASE("deserialization") { std::array v = {{'\"', 0x7F, 0xDF, 0x7F}}; json _; - CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(std::begin(v), std::end(v)), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\x7f\xdf\x7f'"); + CHECK_THROWS_WITH_AS(_ = json::parse(std::begin(v), std::end(v)), "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\x7f\xdf\x7f'", json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -846,13 +828,9 @@ TEST_CASE("deserialization") SECTION("BOM only") { json _; - CHECK_THROWS_AS(_ = json::parse(bom), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(bom), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(_ = json::parse(bom), "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse(std::istringstream(bom)), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(std::istringstream(bom)), - "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(_ = json::parse(std::istringstream(bom)), "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); SaxEventLogger l; CHECK(!json::sax_parse(bom, &l)); @@ -887,13 +865,9 @@ TEST_CASE("deserialization") SECTION("2 byte of BOM") { json _; - CHECK_THROWS_AS(_ = json::parse(bom.substr(0, 2)), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(bom.substr(0, 2)), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'"); + CHECK_THROWS_WITH_AS(_ = json::parse(bom.substr(0, 2)), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse(std::istringstream(bom.substr(0, 2))), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(std::istringstream(bom.substr(0, 2))), - "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'"); + CHECK_THROWS_WITH_AS(_ = json::parse(std::istringstream(bom.substr(0, 2))), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'", json::parse_error&); SaxEventLogger l1; SaxEventLogger l2; @@ -914,13 +888,9 @@ TEST_CASE("deserialization") SECTION("1 byte of BOM") { json _; - CHECK_THROWS_AS(_ = json::parse(bom.substr(0, 1)), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(bom.substr(0, 1)), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'"); + CHECK_THROWS_WITH_AS(_ = json::parse(bom.substr(0, 1)), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse(std::istringstream(bom.substr(0, 1))), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(std::istringstream(bom.substr(0, 1))), - "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'"); + CHECK_THROWS_WITH_AS(_ = json::parse(std::istringstream(bom.substr(0, 1))), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'", json::parse_error&); SaxEventLogger l1; SaxEventLogger l2; diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index f3e79426c..3878b5835 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -64,13 +64,10 @@ TEST_CASE("element access 1") SECTION("access outside bounds") { - CHECK_THROWS_AS(j.at(8), json::out_of_range&); - CHECK_THROWS_AS(j_const.at(8), json::out_of_range&); - - CHECK_THROWS_WITH(j.at(8), - "[json.exception.out_of_range.401] array index 8 is out of range"); - CHECK_THROWS_WITH(j_const.at(8), - "[json.exception.out_of_range.401] array index 8 is out of range"); + CHECK_THROWS_WITH_AS(j.at(8), + "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const.at(8), + "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&); } SECTION("access on non-array type") @@ -79,77 +76,63 @@ TEST_CASE("element access 1") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), json::type_error&); - CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error&); - CHECK_THROWS_WITH(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); } } } @@ -194,8 +177,7 @@ TEST_CASE("element access 1") json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); CHECK_NOTHROW(j_nonarray[0]); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with null"); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with null", json::type_error&); } SECTION("implicit transformation to properly filled array") @@ -210,60 +192,48 @@ TEST_CASE("element access 1") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], json::type_error&); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean"); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean"); + CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], json::type_error&); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string"); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string"); + CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], json::type_error&); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object"); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object"); + CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], json::type_error&); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number"); + CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], json::type_error&); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number"); + CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], json::type_error&); - CHECK_THROWS_AS(j_nonarray_const[0], json::type_error&); - CHECK_THROWS_WITH(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number"); + CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&); } } } @@ -314,9 +284,7 @@ TEST_CASE("element access 1") } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - CHECK_THROWS_AS(jarray.erase(8), json::out_of_range&); - CHECK_THROWS_WITH(jarray.erase(8), - "[json.exception.out_of_range.401] array index 8 is out of range"); + CHECK_THROWS_WITH_AS(jarray.erase(8), "[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&); } } @@ -409,36 +377,28 @@ TEST_CASE("element access 1") { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.begin()), json::invalid_iterator&); - CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), json::invalid_iterator&); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), json::invalid_iterator&); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); + CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jarray.erase(jarray.begin(), jarray2.end()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray.end()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray2.end()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), json::invalid_iterator&); - CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); + CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); } } } @@ -448,57 +408,43 @@ TEST_CASE("element access 1") SECTION("null") { json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with null"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with boolean"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&); } SECTION("string") { json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with string"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&); } SECTION("object") { json j_nonobject(json::value_t::object); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with object"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with object", json::type_error&); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase(0), - "[json.exception.type_error.307] cannot use erase() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); } } } @@ -512,17 +458,13 @@ TEST_CASE("element access 1") { { json j; - CHECK_THROWS_AS(j.front(), json::invalid_iterator&); - CHECK_THROWS_AS(j.back(), json::invalid_iterator&); - CHECK_THROWS_WITH(j.front(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(j.back(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } { const json j{}; - CHECK_THROWS_AS(j.front(), json::invalid_iterator&); - CHECK_THROWS_AS(j.back(), json::invalid_iterator&); - CHECK_THROWS_WITH(j.front(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(j.back(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -603,15 +545,12 @@ TEST_CASE("element access 1") { { json j; - CHECK_THROWS_AS(j.erase(j.begin()), json::type_error&); - CHECK_THROWS_WITH(j.erase(j.begin()), - "[json.exception.type_error.307] cannot use erase() with null"); + CHECK_THROWS_WITH_AS(j.erase(j.begin()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); } { json j; - CHECK_THROWS_AS(j.erase(j.cbegin()), json::type_error&); - CHECK_THROWS_WITH(j.erase(j.begin()), - "[json.exception.type_error.307] cannot use erase() with null"); + CHECK_THROWS_WITH_AS(j.erase(j.begin()), + "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); } } @@ -718,15 +657,11 @@ TEST_CASE("element access 1") { { json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } { json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } } @@ -734,15 +669,11 @@ TEST_CASE("element access 1") { { json j = false; - CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } { json j = true; - CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } } @@ -750,15 +681,11 @@ TEST_CASE("element access 1") { { json j = 17; - CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } { json j = 17; - CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } } @@ -766,15 +693,11 @@ TEST_CASE("element access 1") { { json j = 17u; - CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } { json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } } @@ -782,15 +705,11 @@ TEST_CASE("element access 1") { { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend()), - "[json.exception.invalid_iterator.205] iterator out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&); } } } @@ -801,15 +720,11 @@ TEST_CASE("element access 1") { { json j; - CHECK_THROWS_AS(j.erase(j.begin(), j.end()), json::type_error&); - CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), - "[json.exception.type_error.307] cannot use erase() with null"); + CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.end()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); } { json j; - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), json::type_error&); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), - "[json.exception.type_error.307] cannot use erase() with null"); + CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cend()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); } } @@ -916,17 +831,13 @@ TEST_CASE("element access 1") { { json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -934,17 +845,13 @@ TEST_CASE("element access 1") { { json j = false; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = true; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -952,17 +859,13 @@ TEST_CASE("element access 1") { { json j = 17; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = 17; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -970,17 +873,13 @@ TEST_CASE("element access 1") { { json j = 17u; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } @@ -988,17 +887,13 @@ TEST_CASE("element access 1") { { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } { json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator&); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range"); + CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&); } } } diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 670458cd2..94e0df42d 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -64,12 +64,8 @@ TEST_CASE("element access 2") SECTION("access outside bounds") { - CHECK_THROWS_AS(j.at("foo"), json::out_of_range&); - CHECK_THROWS_AS(j_const.at("foo"), json::out_of_range&); - CHECK_THROWS_WITH(j.at("foo"), - "[json.exception.out_of_range.403] key 'foo' not found"); - CHECK_THROWS_WITH(j_const.at("foo"), - "[json.exception.out_of_range.403] key 'foo' not found"); + CHECK_THROWS_WITH_AS(j.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found", json::out_of_range&); } SECTION("access on non-object type") @@ -78,70 +74,56 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null", json::type_error&); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string", json::type_error&); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array", json::type_error&); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number", json::type_error&); } } } @@ -203,84 +185,56 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with null"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with boolean"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with string"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), - "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); } } } @@ -321,84 +275,56 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with null"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with null", json::type_error&); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with boolean"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with boolean", json::type_error&); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with string"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with string", json::type_error&); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with array", json::type_error&); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject_const.value("/foo"_json_pointer, 1), "[json.exception.type_error.306] cannot use value() with number", json::type_error&); } } } @@ -473,118 +399,91 @@ TEST_CASE("element access 2") const json j_const_nonobject(j_nonobject); CHECK_NOTHROW(j_nonobject["foo"]); CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null", json::type_error&); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); + CHECK_THROWS_WITH_AS(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean", json::type_error&); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); + CHECK_THROWS_WITH_AS(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string", json::type_error&); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); + CHECK_THROWS_WITH_AS(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array", json::type_error&); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_WITH_AS(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_WITH_AS(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error&); - CHECK_THROWS_WITH(j_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_WITH_AS(j_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject["foo"], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_const_nonobject[json::object_t::key_type("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number", json::type_error&); } } } @@ -723,34 +622,26 @@ TEST_CASE("element access 2") { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.begin()), json::invalid_iterator&); - CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), json::invalid_iterator&); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), json::invalid_iterator&); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), json::invalid_iterator&); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); + CHECK_THROWS_WITH_AS(jobject.erase(jobject2.begin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jobject.erase(jobject.begin(), jobject2.end()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jobject.erase(jobject2.begin(), jobject.end()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jobject.erase(jobject2.begin(), jobject2.end()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), json::invalid_iterator&); - CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), json::invalid_iterator&); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), json::invalid_iterator&); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), - "[json.exception.invalid_iterator.203] iterators do not fit current value"); + CHECK_THROWS_WITH_AS(jobject.erase(jobject2.cbegin()), + "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), + "[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&); } } } @@ -760,49 +651,37 @@ TEST_CASE("element access 2") SECTION("null") { json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), - "[json.exception.type_error.307] cannot use erase() with null"); + CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), - "[json.exception.type_error.307] cannot use erase() with boolean"); + CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&); } SECTION("string") { json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), - "[json.exception.type_error.307] cannot use erase() with string"); + CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&); } SECTION("array") { json j_nonobject(json::value_t::array); - CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), - "[json.exception.type_error.307] cannot use erase() with array"); + CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with array", json::type_error&); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), - "[json.exception.type_error.307] cannot use erase() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), - "[json.exception.type_error.307] cannot use erase() with number"); + CHECK_THROWS_WITH_AS(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&); } } } diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index 758e2b5a9..b7cc611b2 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -338,23 +338,17 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(it.value() == json(true)); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(cit.value() == json(true)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(rit.value(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(rit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -542,23 +536,17 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(it.value() == json("hello world")); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(cit.value() == json("hello world")); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(rit.value(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(rit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -739,11 +727,9 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(it.value() == json(1)); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(cit.value() == json(1)); } } @@ -1116,23 +1102,17 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(cit.value() == json(23)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(rit.value(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(rit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -1320,23 +1300,17 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(cit.value() == json(23)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(rit.value(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(rit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -1524,23 +1498,17 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(it.value() == json(23.42)); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); CHECK(cit.value() == json(23.42)); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(rit.value(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(rit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -1598,25 +1566,17 @@ TEST_CASE("iterators 1") { auto it = j.begin(); auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), json::invalid_iterator&); - CHECK_THROWS_AS(it.value(), json::invalid_iterator&); - CHECK_THROWS_AS(cit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(cit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(it.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(cit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(cit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(cit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); auto rit = j.rend(); auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(rit.value(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.key(), json::invalid_iterator&); - CHECK_THROWS_AS(crit.value(), json::invalid_iterator&); - CHECK_THROWS_WITH(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(rit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(rit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.key(), "[json.exception.invalid_iterator.207] cannot use key() for non-object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(crit.value(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } } diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index bf0b35faf..976d0bcda 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -82,32 +82,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 < it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 < it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 < it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 < it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c < it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -127,32 +119,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 <= it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 <= it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 <= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 <= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c <= it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -173,32 +157,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 > it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 > it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 > it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 > it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c > it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -219,32 +195,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 >= it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 >= it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 >= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 >= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c >= it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -269,17 +237,13 @@ TEST_CASE("iterators 2") { if (j != k) { - CHECK_THROWS_AS(j.begin() == k.begin(), json::invalid_iterator&); - CHECK_THROWS_AS(j.cbegin() == k.cbegin(), json::invalid_iterator&); - CHECK_THROWS_AS(j.begin() < k.begin(), json::invalid_iterator&); - CHECK_THROWS_AS(j.cbegin() < k.cbegin(), json::invalid_iterator&); #if JSON_DIAGNOSTICS // the output differs in each loop, so we cannot fix a string for the expected exception #else - CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH_AS(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); #endif } } @@ -299,63 +263,51 @@ TEST_CASE("iterators 2") { { auto it = j_object.begin(); - CHECK_THROWS_AS(it += 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it += 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it + 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it + 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.begin(); - CHECK_THROWS_AS(1 + it, json::invalid_iterator&); - CHECK_THROWS_WITH(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(1 + it, json::invalid_iterator&); - CHECK_THROWS_WITH(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it -= 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it -= 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it - 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.begin(); - CHECK_THROWS_AS(it - it, json::invalid_iterator&); - CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - it, json::invalid_iterator&); - CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } } @@ -444,17 +396,13 @@ TEST_CASE("iterators 2") { { auto it = j_object.begin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&); } { auto it = j_object.cbegin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&); } } @@ -484,17 +432,13 @@ TEST_CASE("iterators 2") { { auto it = j_null.begin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } { auto it = j_null.cbegin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -503,14 +447,12 @@ TEST_CASE("iterators 2") { auto it = j_value.begin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } { auto it = j_value.cbegin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } } @@ -564,32 +506,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 < it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 < it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 < it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 < it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c < it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -609,32 +543,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 <= it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 <= it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 <= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 <= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c <= it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -655,32 +581,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 > it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 > it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 > it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 > it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c > it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -701,32 +619,24 @@ TEST_CASE("iterators 2") { if (j.type() == json::value_t::object) { - CHECK_THROWS_AS(it1 >= it1, json::invalid_iterator&); - CHECK_THROWS_AS(it1 >= it2, json::invalid_iterator&); - CHECK_THROWS_AS(it2 >= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1 >= it3, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c >= it1_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator&); - CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator&); - CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&); #else - CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators"); + CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&); #endif } else @@ -751,17 +661,13 @@ TEST_CASE("iterators 2") { if (j != k) { - CHECK_THROWS_AS(j.rbegin() == k.rbegin(), json::invalid_iterator&); - CHECK_THROWS_AS(j.crbegin() == k.crbegin(), json::invalid_iterator&); - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), json::invalid_iterator&); - CHECK_THROWS_AS(j.crbegin() < k.crbegin(), json::invalid_iterator&); #if JSON_DIAGNOSTICS // the output differs in each loop, so we cannot fix a string for the expected exception #else - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers"); + CHECK_THROWS_WITH_AS(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&); #endif } } @@ -781,63 +687,51 @@ TEST_CASE("iterators 2") { { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it += 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it += 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it + 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it + 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(1 + it, json::invalid_iterator&); - CHECK_THROWS_WITH(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(1 + it, json::invalid_iterator&); - CHECK_THROWS_WITH(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it -= 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it -= 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - 1, json::invalid_iterator&); - CHECK_THROWS_WITH(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - it, json::invalid_iterator&); - CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - it, json::invalid_iterator&); - CHECK_THROWS_WITH(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } } @@ -926,17 +820,13 @@ TEST_CASE("iterators 2") { { auto it = j_object.rbegin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } { auto it = j_object.crbegin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&); } } @@ -966,17 +856,13 @@ TEST_CASE("iterators 2") { { auto it = j_null.rbegin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } { auto it = j_null.crbegin(); - CHECK_THROWS_AS(it[0], json::invalid_iterator&); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[0], "[json.exception.invalid_iterator.214] cannot get value"); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } @@ -985,14 +871,12 @@ TEST_CASE("iterators 2") { auto it = j_value.rbegin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } { auto it = j_value.crbegin(); CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], json::invalid_iterator&); - CHECK_THROWS_WITH(it[1], "[json.exception.invalid_iterator.214] cannot get value"); + CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&); } } } diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 2c6b9cf4b..4ece3a505 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -80,9 +80,7 @@ TEST_CASE("JSON patch") json doc2 = R"({ "q": { "bar": 2 } })"_json; // because "a" does not exist. - CHECK_THROWS_AS(doc2.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(doc2.patch(patch), - "[json.exception.out_of_range.403] key 'a' not found"); + CHECK_THROWS_WITH_AS(doc2.patch(patch), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); } SECTION("4.2 remove") @@ -430,9 +428,7 @@ TEST_CASE("JSON patch") // references neither the root of the document, nor a member of // an existing object, nor a member of an existing array. - CHECK_THROWS_AS(doc.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(doc.patch(patch), - "[json.exception.out_of_range.403] key 'baz' not found"); + CHECK_THROWS_WITH_AS(doc.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); } // A.13. Invalid JSON Patch Document @@ -681,20 +677,17 @@ TEST_CASE("JSON patch") { json j; json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects", json::parse_error&); } SECTION("not an array of objects") { json j; json patch = {"op", "add", "path", "", "value", 1}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects", json::parse_error&); #endif } @@ -702,11 +695,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"foo", "bar"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have member 'op'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have member 'op'", json::parse_error&); #endif } @@ -714,11 +706,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have string member 'op'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have string member 'op'", json::parse_error&); #endif } @@ -726,11 +717,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "foo"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid", json::parse_error&); #endif } } @@ -741,11 +731,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "add"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'", json::parse_error&); #endif } @@ -753,11 +742,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "add"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'", json::parse_error&); #endif } @@ -765,11 +753,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "add"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'", json::parse_error&); #endif } @@ -777,9 +764,7 @@ TEST_CASE("JSON patch") { json j = {1, 2}; json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.401] array index 4 is out of range"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 4 is out of range", json::out_of_range&); } } @@ -789,11 +774,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "remove"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'", json::parse_error&); #endif } @@ -801,11 +785,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "remove"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'", json::parse_error&); #endif } @@ -813,27 +796,21 @@ TEST_CASE("JSON patch") { json j = {1, 2, 3}; json patch = {{{"op", "remove"}, {"path", "/17"}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.401] array index 17 is out of range"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 17 is out of range", json::out_of_range&); } SECTION("nonexisting target location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "remove"}, {"path", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.403] key 'baz' not found"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); } SECTION("root element as target location") { json j = "string"; json patch = {{{"op", "remove"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.405] JSON pointer has no parent"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&); } } @@ -843,11 +820,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "replace"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'", json::parse_error&); #endif } @@ -855,11 +831,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "replace"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'", json::parse_error&); #endif } @@ -867,11 +842,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "replace"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'", json::parse_error&); #endif } @@ -879,18 +853,14 @@ TEST_CASE("JSON patch") { json j = {1, 2, 3}; json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.401] array index 17 is out of range"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 17 is out of range", json::out_of_range&); } SECTION("nonexisting target location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.403] key 'baz' not found"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); } } @@ -900,11 +870,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "move"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'", json::parse_error&); #endif } @@ -912,11 +881,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "move"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'", json::parse_error&); #endif } @@ -926,9 +894,9 @@ TEST_CASE("JSON patch") json patch = {{{"op", "move"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'", json::parse_error&); #endif } @@ -938,9 +906,9 @@ TEST_CASE("JSON patch") json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'", json::parse_error&); #endif } @@ -948,18 +916,14 @@ TEST_CASE("JSON patch") { json j = {1, 2, 3}; json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.401] array index 5 is out of range"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); } SECTION("nonexisting from location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.403] key 'baz' not found"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); } } @@ -969,11 +933,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "copy"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'", json::parse_error&); #endif } @@ -981,11 +944,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "copy"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'", json::parse_error&); #endif } @@ -993,11 +955,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "copy"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'", json::parse_error&); #endif } @@ -1005,11 +966,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'", json::parse_error&); #endif } @@ -1017,18 +977,14 @@ TEST_CASE("JSON patch") { json j = {1, 2, 3}; json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.401] array index 5 is out of range"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); } SECTION("nonexisting from location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), json::out_of_range&); - CHECK_THROWS_WITH(j.patch(patch), - "[json.exception.out_of_range.403] key 'baz' not found"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.out_of_range.403] key 'baz' not found", json::out_of_range&); } } @@ -1038,11 +994,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "test"}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'", json::parse_error&); #endif } @@ -1050,11 +1005,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "test"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'", json::parse_error&); #endif } @@ -1062,11 +1016,10 @@ TEST_CASE("JSON patch") { json j; json patch = {{{"op", "test"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), json::parse_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'", json::parse_error&); #else - CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'"); + CHECK_THROWS_WITH_AS(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'", json::parse_error&); #endif } } diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 41d7d3a9d..8853c7aa6 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -37,33 +37,27 @@ TEST_CASE("JSON pointers") { SECTION("errors") { - CHECK_THROWS_AS(json::json_pointer("foo"), json::parse_error&); - CHECK_THROWS_WITH(json::json_pointer("foo"), - "[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'"); + CHECK_THROWS_WITH_AS(json::json_pointer("foo"), + "[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'", json::parse_error&); - CHECK_THROWS_AS(json::json_pointer("/~~"), json::parse_error&); - CHECK_THROWS_WITH(json::json_pointer("/~~"), - "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'"); + CHECK_THROWS_WITH_AS(json::json_pointer("/~~"), + "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&); - CHECK_THROWS_AS(json::json_pointer("/~"), json::parse_error&); - CHECK_THROWS_WITH(json::json_pointer("/~"), - "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'"); + CHECK_THROWS_WITH_AS(json::json_pointer("/~"), + "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&); json::json_pointer p; - CHECK_THROWS_AS(p.top(), json::out_of_range&); - CHECK_THROWS_WITH(p.top(), - "[json.exception.out_of_range.405] JSON pointer has no parent"); - CHECK_THROWS_AS(p.pop_back(), json::out_of_range&); - CHECK_THROWS_WITH(p.pop_back(), - "[json.exception.out_of_range.405] JSON pointer has no parent"); + CHECK_THROWS_WITH_AS(p.top(), + "[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&); + CHECK_THROWS_WITH_AS(p.pop_back(), + "[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&); SECTION("array index error") { json v = {1, 2, 3, 4}; json::json_pointer ptr("/10e"); - CHECK_THROWS_AS(v[ptr], json::out_of_range&); - CHECK_THROWS_WITH(v[ptr], - "[json.exception.out_of_range.404] unresolved reference token '10e'"); + CHECK_THROWS_WITH_AS(v[ptr], + "[json.exception.out_of_range.404] unresolved reference token '10e'", json::out_of_range&); } } @@ -165,12 +159,10 @@ TEST_CASE("JSON pointers") // unresolved access json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::out_of_range&); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], - "[json.exception.out_of_range.404] unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), - "[json.exception.out_of_range.404] unresolved reference token 'foo'"); + CHECK_THROWS_WITH_AS(j_primitive["/foo"_json_pointer], + "[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_primitive.at("/foo"_json_pointer), + "[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&); CHECK(!j_primitive.contains(json::json_pointer("/foo"))); } @@ -229,18 +221,15 @@ TEST_CASE("JSON pointers") CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); // unescaped access - CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), json::out_of_range&); - CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), - "[json.exception.out_of_range.403] key 'a' not found"); + CHECK_THROWS_WITH_AS(j.at(json::json_pointer("/a/b")), + "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); // unresolved access const json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::out_of_range&); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], - "[json.exception.out_of_range.404] unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), - "[json.exception.out_of_range.404] unresolved reference token 'foo'"); + CHECK_THROWS_WITH_AS(j_primitive["/foo"_json_pointer], + "[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_primitive.at("/foo"_json_pointer), + "[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&); } SECTION("user-defined string literal") @@ -300,18 +289,14 @@ TEST_CASE("JSON pointers") CHECK(j == json({1, 13, 3, 33, nullptr, 55})); // error with leading 0 - CHECK_THROWS_AS(j["/01"_json_pointer], json::parse_error&); - CHECK_THROWS_WITH(j["/01"_json_pointer], - "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); - CHECK_THROWS_AS(j_const["/01"_json_pointer], json::parse_error&); - CHECK_THROWS_WITH(j_const["/01"_json_pointer], - "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); - CHECK_THROWS_AS(j.at("/01"_json_pointer), json::parse_error&); - CHECK_THROWS_WITH(j.at("/01"_json_pointer), - "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); - CHECK_THROWS_AS(j_const.at("/01"_json_pointer), json::parse_error&); - CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), - "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'"); + CHECK_THROWS_WITH_AS(j["/01"_json_pointer], + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&); + CHECK_THROWS_WITH_AS(j_const["/01"_json_pointer], + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&); + CHECK_THROWS_WITH_AS(j.at("/01"_json_pointer), + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&); + CHECK_THROWS_WITH_AS(j_const.at("/01"_json_pointer), + "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&); CHECK(!j.contains("/01"_json_pointer)); CHECK(!j.contains("/01"_json_pointer)); @@ -319,43 +304,33 @@ TEST_CASE("JSON pointers") CHECK(!j_const.contains("/01"_json_pointer)); // error with incorrect numbers - CHECK_THROWS_AS(j["/one"_json_pointer] = 1, json::parse_error&); - CHECK_THROWS_WITH(j["/one"_json_pointer] = 1, - "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); - CHECK_THROWS_AS(j_const["/one"_json_pointer] == 1, json::parse_error&); - CHECK_THROWS_WITH(j_const["/one"_json_pointer] == 1, - "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_WITH_AS(j["/one"_json_pointer] = 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&); + CHECK_THROWS_WITH_AS(j_const["/one"_json_pointer] == 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&); - CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&); - CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1, - "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); - CHECK_THROWS_AS(j_const.at("/one"_json_pointer) == 1, json::parse_error&); - CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1, - "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_WITH_AS(j.at("/one"_json_pointer) = 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&); + CHECK_THROWS_WITH_AS(j_const.at("/one"_json_pointer) == 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&); - CHECK_THROWS_AS(j["/+1"_json_pointer] = 1, json::parse_error&); - CHECK_THROWS_WITH(j["/+1"_json_pointer] = 1, - "[json.exception.parse_error.109] parse error: array index '+1' is not a number"); - CHECK_THROWS_AS(j_const["/+1"_json_pointer] == 1, json::parse_error&); - CHECK_THROWS_WITH(j_const["/+1"_json_pointer] == 1, - "[json.exception.parse_error.109] parse error: array index '+1' is not a number"); + CHECK_THROWS_WITH_AS(j["/+1"_json_pointer] = 1, + "[json.exception.parse_error.109] parse error: array index '+1' is not a number", json::parse_error&); + CHECK_THROWS_WITH_AS(j_const["/+1"_json_pointer] == 1, + "[json.exception.parse_error.109] parse error: array index '+1' is not a number", json::parse_error&); - CHECK_THROWS_AS(j["/1+1"_json_pointer] = 1, json::out_of_range&); - CHECK_THROWS_WITH(j["/1+1"_json_pointer] = 1, - "[json.exception.out_of_range.404] unresolved reference token '1+1'"); - CHECK_THROWS_AS(j_const["/1+1"_json_pointer] == 1, json::out_of_range&); - CHECK_THROWS_WITH(j_const["/1+1"_json_pointer] == 1, - "[json.exception.out_of_range.404] unresolved reference token '1+1'"); + CHECK_THROWS_WITH_AS(j["/1+1"_json_pointer] = 1, + "[json.exception.out_of_range.404] unresolved reference token '1+1'", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const["/1+1"_json_pointer] == 1, + "[json.exception.out_of_range.404] unresolved reference token '1+1'", json::out_of_range&); { 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()); + CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&); } // on some machines, the check below is not constant @@ -369,47 +344,39 @@ TEST_CASE("JSON pointers") 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_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&); } DOCTEST_MSVC_SUPPRESS_WARNING_POP - CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&); - CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1, - "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); - CHECK_THROWS_AS(j_const.at("/one"_json_pointer) == 1, json::parse_error&); - CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1, - "[json.exception.parse_error.109] parse error: array index 'one' is not a number"); + CHECK_THROWS_WITH_AS(j.at("/one"_json_pointer) = 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&); + CHECK_THROWS_WITH_AS(j_const.at("/one"_json_pointer) == 1, + "[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&); CHECK(!j.contains("/one"_json_pointer)); CHECK(!j.contains("/one"_json_pointer)); CHECK(!j_const.contains("/one"_json_pointer)); CHECK(!j_const.contains("/one"_json_pointer)); - CHECK_THROWS_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), json::parse_error&); - CHECK_THROWS_WITH(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), - "[json.exception.parse_error.109] parse error: array index 'three' is not a number"); + CHECK_THROWS_WITH_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), + "[json.exception.parse_error.109] parse error: array index 'three' is not a number", json::parse_error&); // assign to "-" j["/-"_json_pointer] = 99; CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); // error when using "-" in const object - CHECK_THROWS_AS(j_const["/-"_json_pointer], json::out_of_range&); - CHECK_THROWS_WITH(j_const["/-"_json_pointer], - "[json.exception.out_of_range.402] array index '-' (3) is out of range"); + CHECK_THROWS_WITH_AS(j_const["/-"_json_pointer], + "[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&); CHECK(!j_const.contains("/-"_json_pointer)); // error when using "-" with at - CHECK_THROWS_AS(j.at("/-"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), - "[json.exception.out_of_range.402] array index '-' (7) is out of range"); - CHECK_THROWS_AS(j_const.at("/-"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), - "[json.exception.out_of_range.402] array index '-' (3) is out of range"); + CHECK_THROWS_WITH_AS(j.at("/-"_json_pointer), + "[json.exception.out_of_range.402] array index '-' (7) is out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(j_const.at("/-"_json_pointer), + "[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&); CHECK(!j_const.contains("/-"_json_pointer)); } @@ -423,24 +390,20 @@ TEST_CASE("JSON pointers") CHECK(j["/2"_json_pointer] == j[2]); // assign to nonexisting index - CHECK_THROWS_AS(j.at("/3"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j.at("/3"_json_pointer), - "[json.exception.out_of_range.401] array index 3 is out of range"); + CHECK_THROWS_WITH_AS(j.at("/3"_json_pointer), + "[json.exception.out_of_range.401] array index 3 is out of range", json::out_of_range&); CHECK(!j.contains("/3"_json_pointer)); // assign to nonexisting index (with gap) - CHECK_THROWS_AS(j.at("/5"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j.at("/5"_json_pointer), - "[json.exception.out_of_range.401] array index 5 is out of range"); + CHECK_THROWS_WITH_AS(j.at("/5"_json_pointer), + "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); CHECK(!j.contains("/5"_json_pointer)); // assign to "-" - CHECK_THROWS_AS(j["/-"_json_pointer], json::out_of_range&); - CHECK_THROWS_WITH(j["/-"_json_pointer], - "[json.exception.out_of_range.402] array index '-' (3) is out of range"); - CHECK_THROWS_AS(j.at("/-"_json_pointer), json::out_of_range&); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), - "[json.exception.out_of_range.402] array index '-' (3) is out of range"); + CHECK_THROWS_WITH_AS(j["/-"_json_pointer], + "[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&); + CHECK_THROWS_WITH_AS(j.at("/-"_json_pointer), + "[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&); CHECK(!j.contains("/-"_json_pointer)); } } @@ -496,23 +459,20 @@ TEST_CASE("JSON pointers") CHECK(j_flatten.unflatten() == j); // error for nonobjects - CHECK_THROWS_AS(json(1).unflatten(), json::type_error&); - CHECK_THROWS_WITH(json(1).unflatten(), - "[json.exception.type_error.314] only objects can be unflattened"); + CHECK_THROWS_WITH_AS(json(1).unflatten(), + "[json.exception.type_error.314] only objects can be unflattened", json::type_error&); // error for nonprimitve values - CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), json::type_error&); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] (/~11) values in object must be primitive"); + CHECK_THROWS_WITH_AS(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] (/~11) values in object must be primitive", json::type_error&); #else - CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] values in object must be primitive"); + CHECK_THROWS_WITH_AS(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] values in object must be primitive", json::type_error&); #endif // error for conflicting values json j_error = {{"", 42}, {"/foo", 17}}; - CHECK_THROWS_AS(j_error.unflatten(), json::type_error&); - CHECK_THROWS_WITH(j_error.unflatten(), - "[json.exception.type_error.313] invalid value to unflatten"); + CHECK_THROWS_WITH_AS(j_error.unflatten(), + "[json.exception.type_error.313] invalid value to unflatten", json::type_error&); // explicit roundtrip check CHECK(j.flatten().unflatten() == j); diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index ca26930fe..f92954891 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -198,8 +198,7 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j.push_back("Hello"), json::type_error&); - CHECK_THROWS_WITH(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number"); + CHECK_THROWS_WITH_AS(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } @@ -228,8 +227,7 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j.push_back(k), json::type_error&); - CHECK_THROWS_WITH(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number"); + CHECK_THROWS_WITH_AS(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } } @@ -261,9 +259,7 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), json::type_error&); - CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), - "[json.exception.type_error.308] cannot use push_back() with number"); + CHECK_THROWS_WITH_AS(j.push_back(json::object_t::value_type({"one", 1})), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } @@ -298,12 +294,9 @@ TEST_CASE("modifiers") CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); // invalid values (no string/val pair) - CHECK_THROWS_AS(j.push_back({1}), json::type_error&); - CHECK_THROWS_WITH(j.push_back({1}), "[json.exception.type_error.308] cannot use push_back() with object"); - CHECK_THROWS_AS(j.push_back({1, 2}), json::type_error&); - CHECK_THROWS_WITH(j.push_back({1, 2}), "[json.exception.type_error.308] cannot use push_back() with object"); - CHECK_THROWS_AS(j.push_back({1, 2, 3, 4}), json::type_error&); - CHECK_THROWS_WITH(j.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object"); + CHECK_THROWS_WITH_AS(j.push_back({1}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); + CHECK_THROWS_WITH_AS(j.push_back({1, 2}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); + CHECK_THROWS_WITH_AS(j.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); } } } @@ -345,9 +338,7 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j.emplace_back("Hello"), json::type_error&); - CHECK_THROWS_WITH(j.emplace_back("Hello"), - "[json.exception.type_error.311] cannot use emplace_back() with number"); + CHECK_THROWS_WITH_AS(j.emplace_back("Hello"), "[json.exception.type_error.311] cannot use emplace_back() with number", json::type_error&); } } @@ -405,9 +396,7 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j.emplace("foo", "bar"), json::type_error&); - CHECK_THROWS_WITH(j.emplace("foo", "bar"), - "[json.exception.type_error.311] cannot use emplace() with number"); + CHECK_THROWS_WITH_AS(j.emplace("foo", "bar"), "[json.exception.type_error.311] cannot use emplace() with number", json::type_error&); } } @@ -437,8 +426,7 @@ TEST_CASE("modifiers") SECTION("other type") { json j = 1; - CHECK_THROWS_AS(j += "Hello", json::type_error&); - CHECK_THROWS_WITH(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number"); + CHECK_THROWS_WITH_AS(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } @@ -467,8 +455,7 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j += k, json::type_error&); - CHECK_THROWS_WITH(j += k, "[json.exception.type_error.308] cannot use push_back() with number"); + CHECK_THROWS_WITH_AS(j += k, "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } } @@ -500,9 +487,7 @@ TEST_CASE("modifiers") { json j = 1; json k("Hello"); - CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), json::type_error&); - CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), - "[json.exception.type_error.308] cannot use push_back() with number"); + CHECK_THROWS_WITH_AS(j += json::object_t::value_type({"one", 1}), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } @@ -537,8 +522,7 @@ TEST_CASE("modifiers") CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); json k = {{"key1", 1}}; - CHECK_THROWS_AS((k += {1, 2, 3, 4}), json::type_error&); - CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object"); + CHECK_THROWS_WITH_AS((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); } } } @@ -673,15 +657,10 @@ TEST_CASE("modifiers") { json j_other_array2 = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), - json::invalid_iterator&); - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - json::invalid_iterator&); - - CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), - "[json.exception.invalid_iterator.211] passed iterators may not belong to container"); - CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - "[json.exception.invalid_iterator.210] iterators do not fit"); + CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.211] passed iterators may not belong to container", + json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", + json::invalid_iterator&); } } @@ -706,16 +685,9 @@ TEST_CASE("modifiers") { json j_other_array2 = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_object2.begin(), j_object2.end()), json::type_error&); - CHECK_THROWS_AS(j_object1.insert(j_object1.begin(), j_object2.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j_object1.insert(j_array.begin(), j_array.end()), json::invalid_iterator&); - - CHECK_THROWS_WITH(j_array.insert(j_object2.begin(), j_object2.end()), - "[json.exception.type_error.309] cannot use insert() with array"); - CHECK_THROWS_WITH(j_object1.insert(j_object1.begin(), j_object2.end()), - "[json.exception.invalid_iterator.210] iterators do not fit"); - CHECK_THROWS_WITH(j_object1.insert(j_array.begin(), j_array.end()), - "[json.exception.invalid_iterator.202] iterators first and last must point to objects"); + CHECK_THROWS_WITH_AS(j_array.insert(j_object2.begin(), j_object2.end()), "[json.exception.type_error.309] cannot use insert() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_object1.insert(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_object1.insert(j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to objects", json::invalid_iterator&); } } @@ -754,22 +726,11 @@ TEST_CASE("modifiers") // pass iterator to a different array json j_another_array = {1, 2}; json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), json::invalid_iterator&); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), json::invalid_iterator&); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), json::invalid_iterator&); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), json::invalid_iterator&); - - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), - "[json.exception.invalid_iterator.202] iterator does not fit current value"); + CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_value), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10, 11), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); } SECTION("non-array type") @@ -777,20 +738,11 @@ TEST_CASE("modifiers") // call insert on a non-array type json j_nonarray = 3; json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), json::type_error&); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), json::type_error&); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), json::type_error&); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), json::type_error&); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), json::type_error&); - - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), - "[json.exception.type_error.309] cannot use insert() with number"); + CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); + CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); } } @@ -816,11 +768,9 @@ TEST_CASE("modifiers") SECTION("wrong types") { - CHECK_THROWS_AS(j_array.update(j_object1), json::type_error&); - CHECK_THROWS_WITH(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array"); + CHECK_THROWS_WITH_AS(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); - CHECK_THROWS_AS(j_object1.update(j_array), json::type_error&); - CHECK_THROWS_WITH(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array"); + CHECK_THROWS_WITH_AS(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); } } @@ -846,16 +796,9 @@ TEST_CASE("modifiers") { json j_other_array2 = {"first", "second"}; - CHECK_THROWS_AS(j_array.update(j_object2.begin(), j_object2.end()), json::type_error&); - CHECK_THROWS_AS(j_object1.update(j_object1.begin(), j_object2.end()), json::invalid_iterator&); - CHECK_THROWS_AS(j_object1.update(j_array.begin(), j_array.end()), json::type_error&); - - CHECK_THROWS_WITH(j_array.update(j_object2.begin(), j_object2.end()), - "[json.exception.type_error.312] cannot use update() with array"); - CHECK_THROWS_WITH(j_object1.update(j_object1.begin(), j_object2.end()), - "[json.exception.invalid_iterator.210] iterators do not fit"); - CHECK_THROWS_WITH(j_object1.update(j_array.begin(), j_array.end()), - "[json.exception.type_error.312] cannot use update() with array"); + CHECK_THROWS_WITH_AS(j_array.update(j_object2.begin(), j_object2.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); + CHECK_THROWS_WITH_AS(j_object1.update(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&); + CHECK_THROWS_WITH_AS(j_object1.update(j_array.begin(), j_array.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); } } } @@ -932,8 +875,7 @@ TEST_CASE("modifiers") json j = 17; json::array_t a = {"foo", "bar", "baz"}; - CHECK_THROWS_AS(j.swap(a), json::type_error&); - CHECK_THROWS_WITH(j.swap(a), "[json.exception.type_error.310] cannot use swap() with number"); + CHECK_THROWS_WITH_AS(j.swap(a), "[json.exception.type_error.310] cannot use swap() with number", json::type_error&); } } @@ -958,8 +900,7 @@ TEST_CASE("modifiers") json j = 17; json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; - CHECK_THROWS_AS(j.swap(o), json::type_error&); - CHECK_THROWS_WITH(j.swap(o), "[json.exception.type_error.310] cannot use swap() with number"); + CHECK_THROWS_WITH_AS(j.swap(o), "[json.exception.type_error.310] cannot use swap() with number", json::type_error&); } } @@ -984,8 +925,7 @@ TEST_CASE("modifiers") json j = 17; json::string_t s = "Hallo Welt"; - CHECK_THROWS_AS(j.swap(s), json::type_error&); - CHECK_THROWS_WITH(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number"); + CHECK_THROWS_WITH_AS(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number", json::type_error&); } } diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 70bd6a0f1..483f8ed46 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1425,76 +1425,54 @@ TEST_CASE("MessagePack") SECTION("empty byte vector") { json _; - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector()), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector()), - "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector()), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&); CHECK(json::from_msgpack(std::vector(), true, false).is_discarded()); } SECTION("too short byte vector") { json _; - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0x87})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcc})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcd})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcd, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xce})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xce, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xce, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xce, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xa5, 0x68, 0x65})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0x92, 0x01})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0x81, 0xa1, 0x61})), json::parse_error&); - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xc4, 0x02})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0x87})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcc})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcd})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcd, 0x00})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xce})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xce, 0x00})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xce, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xce, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf})), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), - "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing MessagePack number: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xa5, 0x68, 0x65})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack string: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0x92, 0x01})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack value: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0x81, 0xa1, 0x61})), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack value: unexpected end of input"); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xc4, 0x02})), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack binary: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0x87})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcc})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcd})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcd, 0x00})), + "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xce})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xce, 0x00})), + "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xce, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xce, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00})), + "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), + "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xa5, 0x68, 0x65})), + "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0x92, 0x01})), + "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0x81, 0xa1, 0x61})), + "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xc4, 0x02})), + "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack binary: unexpected end of input", json::parse_error&); CHECK(json::from_msgpack(std::vector({0x87}), true, false).is_discarded()); CHECK(json::from_msgpack(std::vector({0xcc}), true, false).is_discarded()); @@ -1524,9 +1502,7 @@ TEST_CASE("MessagePack") SECTION("concrete examples") { json _; - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xc1})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xc1})), - "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing MessagePack value: invalid byte: 0xC1"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0xc1})), "[json.exception.parse_error.112] parse error at byte 1: syntax error while parsing MessagePack value: invalid byte: 0xC1", json::parse_error&); } SECTION("all unsupported bytes") @@ -1547,9 +1523,7 @@ TEST_CASE("MessagePack") SECTION("invalid string in map") { json _; - CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0x81, 0xff, 0x01})), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0x81, 0xff, 0x01})), - "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack string: expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xFF"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector({0x81, 0xff, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack string: expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xFF", json::parse_error&); CHECK(json::from_msgpack(std::vector({0x81, 0xff, 0x01}), true, false).is_discarded()); } @@ -1565,9 +1539,7 @@ TEST_CASE("MessagePack") SECTION("strict mode") { json _; - CHECK_THROWS_AS(_ = json::from_msgpack(vec), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(vec), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack value: expected end of input; last byte: 0xC0"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack value: expected end of input; last byte: 0xC0", json::parse_error&); CHECK(json::from_msgpack(vec, true, false).is_discarded()); } } diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index 1342d479d..c71ad5bc1 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -65,24 +65,18 @@ TEST_CASE("reference access") // check if mismatching references throw correctly CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&); } SECTION("const reference access to const object_t") @@ -114,25 +108,19 @@ TEST_CASE("reference access") CHECK(p2 == value.get()); // check if mismatching references throw correctly - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&); CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&); } SECTION("reference access to string_t") @@ -150,25 +138,19 @@ TEST_CASE("reference access") CHECK(p2 == value.get()); // check if mismatching references throw correctly - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&); CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&); } SECTION("reference access to boolean_t") @@ -186,25 +168,19 @@ TEST_CASE("reference access") CHECK(p2 == value.get()); // check if mismatching references throw correctly - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&); CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&); } SECTION("reference access to number_integer_t") @@ -222,25 +198,19 @@ TEST_CASE("reference access") CHECK(p2 == value.get()); // check if mismatching references throw correctly - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); } SECTION("reference access to number_unsigned_t") @@ -258,25 +228,18 @@ TEST_CASE("reference access") CHECK(p2 == value.get()); // check if mismatching references throw correctly - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - //CHECK_THROWS_AS(value.get_ref(), json::type_error&); - //CHECK_THROWS_WITH(value.get_ref(), - // "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), + "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + //CHECK_THROWS_WITH_AS(value.get_ref(), + // "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); } SECTION("reference access to number_float_t") @@ -294,24 +257,12 @@ TEST_CASE("reference access") CHECK(p2 == value.get()); // check if mismatching references throw correctly - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); - CHECK_THROWS_AS(value.get_ref(), json::type_error&); - CHECK_THROWS_WITH(value.get_ref(), - "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number"); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); + CHECK_THROWS_WITH_AS(value.get_ref(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&); CHECK_NOTHROW(value.get_ref()); } } diff --git a/test/src/unit-regression1.cpp b/test/src/unit-regression1.cpp index 2d22b5791..a83be81b8 100644 --- a/test/src/unit-regression1.cpp +++ b/test/src/unit-regression1.cpp @@ -391,11 +391,10 @@ TEST_CASE("regression tests 1") // improve coverage o["int"] = 1; - CHECK_THROWS_AS(s2 = o["int"], json::type_error); #if JSON_DIAGNOSTICS - CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] (/int) type must be string, but is number"); + CHECK_THROWS_WITH_AS(s2 = o["int"], "[json.exception.type_error.302] (/int) type must be string, but is number", json::type_error); #else - CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number"); + CHECK_THROWS_WITH_AS(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number", json::type_error); #endif } #endif @@ -675,9 +674,7 @@ TEST_CASE("regression tests 1") SECTION("issue #329 - serialized value not always can be parsed") { json _; - CHECK_THROWS_AS(_ = json::parse("22e2222"), json::out_of_range&); - CHECK_THROWS_WITH(_ = json::parse("22e2222"), - "[json.exception.out_of_range.406] number overflow parsing '22e2222'"); + CHECK_THROWS_WITH_AS(_ = json::parse("22e2222"), "[json.exception.out_of_range.406] number overflow parsing '22e2222'", json::out_of_range&); } SECTION("issue #360 - Loss of precision when serializing ") @@ -742,8 +739,7 @@ TEST_CASE("regression tests 1") { std::ifstream f("file_not_found.json"); json _; - CHECK_THROWS_AS(_ = json::parse(f), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse(f), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(_ = json::parse(f), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("issue #367 - calling stream at EOF") @@ -757,9 +753,7 @@ TEST_CASE("regression tests 1") // ss is not at EOF; this yielded an error before the fix // (threw basic_string::append). No, it should just throw // a parse error because of the EOF. - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("issue #367 - behavior of operator>> should more closely resemble that of built-in overloads") @@ -768,9 +762,7 @@ TEST_CASE("regression tests 1") { std::stringstream ss; json j; - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("(whitespace)") @@ -791,9 +783,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 111); - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("one value + whitespace") @@ -817,9 +807,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 333); - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("three values") @@ -834,9 +822,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 333); - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("literals without whitespace") @@ -853,9 +839,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == ""); - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("example from #529") @@ -868,9 +852,7 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == json({{"three", 3}})); - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("second example from #529") @@ -939,9 +921,7 @@ TEST_CASE("regression tests 1") // original test case std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; json _; - CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec), - "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing CBOR string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); } SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)") @@ -950,33 +930,23 @@ TEST_CASE("regression tests 1") // original test case: incomplete float64 std::vector vec1 {0xcb, 0x8f, 0x0a}; - CHECK_THROWS_AS(_ = json::from_msgpack(vec1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(vec1), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec1), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); // related test case: incomplete float32 std::vector vec2 {0xca, 0x8f, 0x0a}; - CHECK_THROWS_AS(_ = json::from_msgpack(vec2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(vec2), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec2), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); // related test case: incomplete Half-Precision Float (CBOR) std::vector vec3 {0xf9, 0x8f}; - CHECK_THROWS_AS(_ = json::from_cbor(vec3), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec3), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); // related test case: incomplete Single-Precision Float (CBOR) std::vector vec4 {0xfa, 0x8f, 0x0a}; - CHECK_THROWS_AS(_ = json::from_cbor(vec4), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec4), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec4), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); // related test case: incomplete Double-Precision Float (CBOR) std::vector vec5 {0xfb, 0x8f, 0x0a}; - CHECK_THROWS_AS(_ = json::from_cbor(vec5), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec5), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec5), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); } SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)") @@ -985,9 +955,7 @@ TEST_CASE("regression tests 1") // original test case std::vector vec1 {0x87}; - CHECK_THROWS_AS(_ = json::from_msgpack(vec1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(vec1), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec1), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&); // more test cases for MessagePack for (auto b : @@ -1019,12 +987,8 @@ TEST_CASE("regression tests 1") // special case: empty input std::vector vec2; - CHECK_THROWS_AS(_ = json::from_cbor(vec2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec2), - "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing CBOR value: unexpected end of input"); - CHECK_THROWS_AS(_ = json::from_msgpack(vec2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_msgpack(vec2), - "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); + CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec2), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&); } SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)") @@ -1033,21 +997,15 @@ TEST_CASE("regression tests 1") // original test case: empty UTF-8 string (indefinite length) std::vector vec1 {0x7f}; - CHECK_THROWS_AS(_ = json::from_cbor(vec1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec1), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); // related test case: empty array (indefinite length) std::vector vec2 {0x9f}; - CHECK_THROWS_AS(_ = json::from_cbor(vec2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec2), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); // related test case: empty map (indefinite length) std::vector vec3 {0xbf}; - CHECK_THROWS_AS(_ = json::from_cbor(vec3), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec3), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); } SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)") @@ -1075,27 +1033,19 @@ TEST_CASE("regression tests 1") }; json _; - CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec), - "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x98"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x98", json::parse_error&); // related test case: nonempty UTF-8 string (indefinite length) std::vector vec1 {0x7f, 0x61, 0x61}; - CHECK_THROWS_AS(_ = json::from_cbor(vec1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec1), - "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); // related test case: nonempty array (indefinite length) std::vector vec2 {0x9f, 0x01}; - CHECK_THROWS_AS(_ = json::from_cbor(vec2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec2), - "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); // related test case: nonempty map (indefinite length) std::vector vec3 {0xbf, 0x61, 0x61, 0x01}; - CHECK_THROWS_AS(_ = json::from_cbor(vec3), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec3), - "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); } SECTION("issue #414 - compare with literal 0)") @@ -1130,9 +1080,7 @@ TEST_CASE("regression tests 1") }; json _; - CHECK_THROWS_AS(_ = json::from_cbor(vec1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec1), - "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4", json::parse_error&); // related test case: double-precision std::vector vec2 @@ -1144,9 +1092,7 @@ TEST_CASE("regression tests 1") 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb }; - CHECK_THROWS_AS(_ = json::from_cbor(vec2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_cbor(vec2), - "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4"); + CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4", json::parse_error&); } SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)") @@ -1196,10 +1142,8 @@ TEST_CASE("regression tests 1") }; CHECK_NOTHROW(create(j_array)); - CHECK_THROWS_AS(create(j_number), json::type_error&); - CHECK_THROWS_WITH(create(j_number), "[json.exception.type_error.302] type must be array, but is number"); - CHECK_THROWS_AS(create(j_null), json::type_error&); - CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } SECTION("std::list") @@ -1210,10 +1154,8 @@ TEST_CASE("regression tests 1") }; CHECK_NOTHROW(create(j_array)); - CHECK_THROWS_AS(create(j_number), json::type_error&); - CHECK_THROWS_WITH(create(j_number), "[json.exception.type_error.302] type must be array, but is number"); - CHECK_THROWS_AS(create(j_null), json::type_error&); - CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } SECTION("std::forward_list") @@ -1224,10 +1166,8 @@ TEST_CASE("regression tests 1") }; CHECK_NOTHROW(create(j_array)); - CHECK_THROWS_AS(create(j_number), json::type_error&); - CHECK_THROWS_WITH(create(j_number), "[json.exception.type_error.302] type must be array, but is number"); - CHECK_THROWS_AS(create(j_null), json::type_error&); - CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&); + CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } } #endif @@ -1376,9 +1316,7 @@ TEST_CASE("regression tests 1") CHECK(v[i] == j[i]); } - CHECK_THROWS_AS(json().get>(), json::type_error&); - CHECK_THROWS_WITH(json().get>(), - "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH_AS(json().get>(), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } } #endif @@ -1448,8 +1386,7 @@ TEST_CASE("regression tests 1") std::array key1 = {{ 103, 92, 117, 48, 48, 48, 55, 92, 114, 215, 126, 214, 95, 92, 34, 174, 40, 71, 38, 174, 40, 71, 38, 223, 134, 247, 127, 0 }}; std::string key1_str(reinterpret_cast(key1.data())); json j = key1_str; - CHECK_THROWS_AS(j.dump(), json::type_error&); - CHECK_THROWS_WITH(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E"); + CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&); } #if JSON_USE_IMPLICIT_CONVERSIONS diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index b07c3494f..5ba1935d6 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -104,10 +104,8 @@ TEST_CASE("serialization") { json j = "ä\xA9ü"; - CHECK_THROWS_AS(j.dump(), json::type_error&); - CHECK_THROWS_WITH(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9"); - CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); - CHECK_THROWS_WITH(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9"); + CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); + CHECK_THROWS_WITH_AS(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\""); @@ -117,8 +115,7 @@ TEST_CASE("serialization") { json j = "123\xC2"; - CHECK_THROWS_AS(j.dump(), json::type_error&); - CHECK_THROWS_WITH(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2"); + CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\""); @@ -129,8 +126,7 @@ TEST_CASE("serialization") { json j = "123\xF1\xB0\x34\x35\x36"; - CHECK_THROWS_AS(j.dump(), json::type_error&); - CHECK_THROWS_WITH(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34"); + CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&); CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\""); diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp index b5657284c..d60edf665 100644 --- a/test/src/unit-ubjson.cpp +++ b/test/src/unit-ubjson.cpp @@ -1595,9 +1595,7 @@ TEST_CASE("UBJSON") SECTION("strict mode") { json _; - CHECK_THROWS_AS(_ = json::from_ubjson(vec), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vec), - "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: expected end of input; last byte: 0x5A"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: expected end of input; last byte: 0x5A", json::parse_error&); } } @@ -1820,9 +1818,7 @@ TEST_CASE("UBJSON") SECTION("empty byte vector") { json _; - CHECK_THROWS_AS(_ = json::from_ubjson(std::vector()), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(std::vector()), - "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(std::vector()), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); } SECTION("char") @@ -1831,16 +1827,14 @@ TEST_CASE("UBJSON") { std::vector v = {'C'}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON char: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON char: unexpected end of input", json::parse_error&); } SECTION("byte out of range") { std::vector v = {'C', 130}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82", json::parse_error&); } } @@ -1850,16 +1844,14 @@ TEST_CASE("UBJSON") { std::vector v = {'S'}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); } SECTION("invalid byte") { std::vector v = {'S', '1', 'a'}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&); } } @@ -1869,8 +1861,7 @@ TEST_CASE("UBJSON") { std::vector v = {'[', '$', 'i', 2}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing UBJSON size: expected '#' after type information; last byte: 0x02"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing UBJSON size: expected '#' after type information; last byte: 0x02", json::parse_error&); } } @@ -1878,18 +1869,15 @@ TEST_CASE("UBJSON") { std::vector vS = {'S'}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(vS), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vS, true, false).is_discarded()); std::vector v = {'S', 'i', '2', 'a'}; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing UBJSON string: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing UBJSON string: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(v, true, false).is_discarded()); std::vector vC = {'C'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vC), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vC), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON char: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vC), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON char: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vC, true, false).is_discarded()); } @@ -1897,33 +1885,27 @@ TEST_CASE("UBJSON") { std::vector vU = {'[', '#', 'U'}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(vU), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vU, true, false).is_discarded()); std::vector vi = {'[', '#', 'i'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vi), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vi, true, false).is_discarded()); std::vector vI = {'[', '#', 'I'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vI), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vI), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vI), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vI, true, false).is_discarded()); std::vector vl = {'[', '#', 'l'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vl), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vl), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vl), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vl, true, false).is_discarded()); std::vector vL = {'[', '#', 'L'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vL), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vL), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vL), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vL, true, false).is_discarded()); std::vector v0 = {'[', '#', 'T', ']'}; - CHECK_THROWS_AS(_ = json::from_ubjson(v0), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x54"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x54", json::parse_error&); CHECK(json::from_ubjson(v0, true, false).is_discarded()); } @@ -1931,18 +1913,15 @@ TEST_CASE("UBJSON") { std::vector v0 = {'[', '$'}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(v0), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v0), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing UBJSON type: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v0), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing UBJSON type: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(v0, true, false).is_discarded()); std::vector vi = {'[', '$', '#'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vi), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vi, true, false).is_discarded()); std::vector vT = {'[', '$', 'T'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vT), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vT), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vT), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vT, true, false).is_discarded()); } @@ -1950,18 +1929,15 @@ TEST_CASE("UBJSON") { std::vector vST = {'[', '$', 'i', '#', 'i', 2, 1}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(vST), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vST), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vST), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vST, true, false).is_discarded()); std::vector vS = {'[', '#', 'i', 2, 'i', 1}; - CHECK_THROWS_AS(_ = json::from_ubjson(vS), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vS, true, false).is_discarded()); std::vector v = {'[', 'i', 2, 'i', 1}; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(v, true, false).is_discarded()); } @@ -1969,43 +1945,35 @@ TEST_CASE("UBJSON") { std::vector vST = {'{', '$', 'i', '#', 'i', 2, 'i', 1, 'a', 1}; json _; - CHECK_THROWS_AS(_ = json::from_ubjson(vST), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vST), "[json.exception.parse_error.110] parse error at byte 11: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vST), "[json.exception.parse_error.110] parse error at byte 11: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vST, true, false).is_discarded()); std::vector vT = {'{', '$', 'i', 'i', 1, 'a', 1}; - CHECK_THROWS_AS(_ = json::from_ubjson(vT), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vT), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing UBJSON size: expected '#' after type information; last byte: 0x69"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vT), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing UBJSON size: expected '#' after type information; last byte: 0x69", json::parse_error&); CHECK(json::from_ubjson(vT, true, false).is_discarded()); std::vector vS = {'{', '#', 'i', 2, 'i', 1, 'a', 'i', 1}; - CHECK_THROWS_AS(_ = json::from_ubjson(vS), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vS, true, false).is_discarded()); std::vector v = {'{', 'i', 1, 'a', 'i', 1}; - CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(v, true, false).is_discarded()); std::vector v2 = {'{', 'i', 1, 'a', 'i', 1, 'i'}; - CHECK_THROWS_AS(_ = json::from_ubjson(v2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(v2, true, false).is_discarded()); std::vector v3 = {'{', 'i', 1, 'a'}; - CHECK_THROWS_AS(_ = json::from_ubjson(v3), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(v3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(v3, true, false).is_discarded()); std::vector vST1 = {'{', '$', 'd', '#', 'i', 2, 'i', 1, 'a'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vST1), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vST1), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing UBJSON number: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vST1), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing UBJSON number: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vST1, true, false).is_discarded()); std::vector vST2 = {'{', '#', 'i', 2, 'i', 1, 'a'}; - CHECK_THROWS_AS(_ = json::from_ubjson(vST2), json::parse_error&); - CHECK_THROWS_WITH(_ = json::from_ubjson(vST2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing UBJSON value: unexpected end of input"); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vST2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&); CHECK(json::from_ubjson(vST2, true, false).is_discarded()); } } diff --git a/test/src/unit-unicode1.cpp b/test/src/unit-unicode1.cpp index 3a736b0be..a988ba890 100644 --- a/test/src/unit-unicode1.cpp +++ b/test/src/unit-unicode1.cpp @@ -107,33 +107,19 @@ TEST_CASE("Unicode (1/5)" * doctest::skip()) { json _; - CHECK_THROWS_AS(_ = json::parse("\"\\uDC00\\uDC00\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uDC00\\uDC00\""), - "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uDC00'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uDC00\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uDC00'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD7FF\\uDC00\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD7FF\\uDC00\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uD7FF\\uDC00'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD7FF\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uD7FF\\uDC00'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD800]\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD800]\""), - "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800]'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800]\""), "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800]'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD800\\v\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD800\\v\""), - "[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\v'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\v\""), "[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\v'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD800\\u123\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD800\\u123\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\uD800\\u123\"'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\u123\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\uD800\\u123\"'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD800\\uDBFF\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD800\\uDBFF\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uDBFF'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uDBFF\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uDBFF'", json::parse_error&); - CHECK_THROWS_AS(_ = json::parse("\"\\uD800\\uE000\""), json::parse_error&); - CHECK_THROWS_WITH(_ = json::parse("\"\\uD800\\uE000\""), - "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uE000'"); + CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uE000\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uE000'", json::parse_error&); } } From f7490e33da7ab0c27214201a003e23b861b4e97d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 25 Mar 2022 10:10:21 +0100 Subject: [PATCH 028/110] :wrench: overwork issue template #3348 --- .github/ISSUE_TEMPLATE/bug.yaml | 106 ++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.yaml diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml new file mode 100644 index 000000000..495797347 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -0,0 +1,106 @@ +name: Bug Report +description: File a bug report +labels: + - 'kind: bug' +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Make sure you give it a short and specific **title** so that the report + is searchable and uniquely identifiable. + + Note that this form is for bug reports only. Please + [open a discussion](https://github.com/nlohmann/json/discussions/new) + for questions, feature requests, or support requests + - type: textarea + id: summary + attributes: + label: Description + description: | + Please provide an abstract description of the issue to the developers, + and why you consider it to be a bug. Please include any specific links + to the documentation, JSON specification, or code. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Reproduction steps + description: | + How do you trigger the bug? Please walk us through step by step. Be as + specific as possible. + placeholder: | + 1. + 2. + 3. + ... + validations: + required: true + - type: textarea + id: results + attributes: + label: Expected vs. actual results + description: | + Please describe what you expected to happen after the steps above and + what actually happened. + placeholder: | + Expected behavior: + + Actual behavior: + validations: + required: true + - type: textarea + id: code + attributes: + label: Minimal code example + description: | + If possible, provide a small and self-contained example that triggers + the bug. Please understand that we cannot analyze and debug large code + bases. Please do not paste screenshots here. + render: Shell + - type: textarea + id: output + attributes: + label: Error messages + description: | + Please provide any kind of error output (compilation errors, exception + messages, stack traces, etc.) which can help to diagnose the error. + render: Shell + - type: input + id: compiler + attributes: + label: Compiler and operating system + description: | + On which operating systems and compilers have you observed the issue? + Include as many relevant details about the environment you experienced + the bug in. Make sure you use a + [supported compiler](https://github.com/nlohmann/json#supported-compilers). + placeholder: | + macOS 12.3 using g++ 11.2.0 and Apple Clang 13.1.6 + validations: + required: true + - type: input + id: version + attributes: + label: Library version + description: | + Which version of the library did you use? If it is a released version, + please enter the version number (e.g., 3.10.5). Otherwise, please enter + the commit hash. If you got the library from another source as the + GitHub repository (e.g., via a package manager), please also state + this. + placeholder: | + release version 3.10.5 + validations: + required: true + - type: checkboxes + id: validation + attributes: + label: Validation + description: | + Please check these additional steps: + options: + - label: The bug also occurs if the latest version from the [`develop`](https://github.com/nlohmann/json/tree/develop) branch is used. + - label: I can successfully [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests) From 1ba736893e3656d505f346e22d11024459b4a149 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 25 Mar 2022 10:12:33 +0100 Subject: [PATCH 029/110] :wrench: overwork issue template #3348 --- .github/ISSUE_TEMPLATE/bug.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index 495797347..aa8a5cfe6 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -5,7 +5,7 @@ labels: body: - type: markdown attributes: - value: | + value: > Thanks for taking the time to fill out this bug report! Make sure you give it a short and specific **title** so that the report @@ -18,7 +18,7 @@ body: id: summary attributes: label: Description - description: | + description: > Please provide an abstract description of the issue to the developers, and why you consider it to be a bug. Please include any specific links to the documentation, JSON specification, or code. @@ -28,10 +28,10 @@ body: id: reproduce attributes: label: Reproduction steps - description: | + description: > How do you trigger the bug? Please walk us through step by step. Be as specific as possible. - placeholder: | + placeholder: > 1. 2. 3. @@ -42,10 +42,10 @@ body: id: results attributes: label: Expected vs. actual results - description: | + description: > Please describe what you expected to happen after the steps above and what actually happened. - placeholder: | + placeholder: > Expected behavior: Actual behavior: @@ -55,7 +55,7 @@ body: id: code attributes: label: Minimal code example - description: | + description: > If possible, provide a small and self-contained example that triggers the bug. Please understand that we cannot analyze and debug large code bases. Please do not paste screenshots here. @@ -64,7 +64,7 @@ body: id: output attributes: label: Error messages - description: | + description: > Please provide any kind of error output (compilation errors, exception messages, stack traces, etc.) which can help to diagnose the error. render: Shell @@ -72,12 +72,12 @@ body: id: compiler attributes: label: Compiler and operating system - description: | + description: > On which operating systems and compilers have you observed the issue? Include as many relevant details about the environment you experienced the bug in. Make sure you use a [supported compiler](https://github.com/nlohmann/json#supported-compilers). - placeholder: | + placeholder: > macOS 12.3 using g++ 11.2.0 and Apple Clang 13.1.6 validations: required: true @@ -85,13 +85,13 @@ body: id: version attributes: label: Library version - description: | + description: > Which version of the library did you use? If it is a released version, please enter the version number (e.g., 3.10.5). Otherwise, please enter the commit hash. If you got the library from another source as the GitHub repository (e.g., via a package manager), please also state this. - placeholder: | + placeholder: > release version 3.10.5 validations: required: true @@ -99,7 +99,7 @@ body: id: validation attributes: label: Validation - description: | + description: > Please check these additional steps: options: - label: The bug also occurs if the latest version from the [`develop`](https://github.com/nlohmann/json/tree/develop) branch is used. From 66f6b4b6a0b897e91f7fdd5f6058a8c62137895f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 25 Mar 2022 10:16:21 +0100 Subject: [PATCH 030/110] :wrench: overwork issue template #3348 --- .github/ISSUE_TEMPLATE/Bug_report.md | 57 ---------------------------- .github/ISSUE_TEMPLATE/bug.yaml | 17 +-------- 2 files changed, 2 insertions(+), 72 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md deleted file mode 100644 index d0fbbdda0..000000000 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: 'kind: bug' -assignees: '' - ---- - - - -#### What is the issue you have? - - - - -#### Please describe the steps to reproduce the issue. - - - - -1. -2. -3. - -#### Can you provide a small but working code example? - - - -#### What is the expected behavior? - - - -#### And what is the actual behavior instead? - - - -#### Which compiler and operating system are you using? - - - - -- Compiler: ___ -- Operating system: ___ - -#### Which version of the library did you use? - - - -- [ ] latest release version 3.10.5 -- [ ] other release - please state the version: ___ -- [ ] the `develop` branch - -#### If you experience a compilation error: can you [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests)? - -- [ ] yes -- [ ] no - please copy/paste the error message below diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index aa8a5cfe6..9ede0b3d7 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -1,5 +1,5 @@ name: Bug Report -description: File a bug report +description: Create a bug report labels: - 'kind: bug' body: @@ -31,11 +31,6 @@ body: description: > How do you trigger the bug? Please walk us through step by step. Be as specific as possible. - placeholder: > - 1. - 2. - 3. - ... validations: required: true - type: textarea @@ -45,10 +40,6 @@ body: description: > Please describe what you expected to happen after the steps above and what actually happened. - placeholder: > - Expected behavior: - - Actual behavior: validations: required: true - type: textarea @@ -77,8 +68,6 @@ body: Include as many relevant details about the environment you experienced the bug in. Make sure you use a [supported compiler](https://github.com/nlohmann/json#supported-compilers). - placeholder: > - macOS 12.3 using g++ 11.2.0 and Apple Clang 13.1.6 validations: required: true - type: input @@ -91,8 +80,6 @@ body: the commit hash. If you got the library from another source as the GitHub repository (e.g., via a package manager), please also state this. - placeholder: > - release version 3.10.5 validations: required: true - type: checkboxes @@ -103,4 +90,4 @@ body: Please check these additional steps: options: - label: The bug also occurs if the latest version from the [`develop`](https://github.com/nlohmann/json/tree/develop) branch is used. - - label: I can successfully [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests) + - label: I can successfully [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests). From 3b16057ffaaed250cf207233f9238392ea0245ee Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Fri, 1 Apr 2022 14:38:17 +0200 Subject: [PATCH 031/110] Refactor unit tests to use more convenient doctest assertion macros (Part 2) (#3405) * Refactor assertion and adjust expected error message * Refactor assertion and adjust expected error message * Refactor assertion and remove redundant local variable * Refactor assertion and remove redundant local variable * Rename local variable * Apply formatting --- test/src/unit-regression1.cpp | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/test/src/unit-regression1.cpp b/test/src/unit-regression1.cpp index a83be81b8..e6dfaa1c5 100644 --- a/test/src/unit-regression1.cpp +++ b/test/src/unit-regression1.cpp @@ -770,9 +770,8 @@ TEST_CASE("regression tests 1") std::stringstream ss; ss << " "; json j; - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, + "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("one value") @@ -794,9 +793,8 @@ TEST_CASE("regression tests 1") CHECK_NOTHROW(ss >> j); CHECK(j == 222); - CHECK_THROWS_AS(ss >> j, json::parse_error&); - CHECK_THROWS_WITH(ss >> j, - "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"); + CHECK_THROWS_WITH_AS(ss >> j, + "[json.exception.parse_error.101] parse error at line 2, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("whitespace + one value") @@ -1413,24 +1411,14 @@ TEST_CASE("regression tests 1") auto p1 = R"([{"op": "move", "from": "/one/two/three", "path": "/a/b/c"}])"_json; - CHECK_THROWS_AS(model.patch(p1), json::out_of_range&); + CHECK_THROWS_WITH_AS(model.patch(p1), + "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); - auto p2 = R"([{"op": "move", + auto p2 = R"([{"op": "copy", "from": "/one/two/three", "path": "/a/b/c"}])"_json; - CHECK_THROWS_WITH(model.patch(p2), - "[json.exception.out_of_range.403] key 'a' not found"); - - auto p3 = R"([{"op": "copy", - "from": "/one/two/three", - "path": "/a/b/c"}])"_json; - CHECK_THROWS_AS(model.patch(p3), json::out_of_range&); - - auto p4 = R"([{"op": "copy", - "from": "/one/two/three", - "path": "/a/b/c"}])"_json; - CHECK_THROWS_WITH(model.patch(p4), - "[json.exception.out_of_range.403] key 'a' not found"); + CHECK_THROWS_WITH_AS(model.patch(p2), + "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); } SECTION("issue #961 - incorrect parsing of indefinite length CBOR strings") From 6121dbbe0282b73e72e78c2572c6efb8cc82da32 Mon Sep 17 00:00:00 2001 From: DarkZeros Date: Sun, 3 Apr 2022 17:33:33 +0100 Subject: [PATCH 032/110] Avoid clash with Arduino defines (#3338) * Avoid clash with Arduino defines * Arduino defines B0/B1 in "binary.h" #define B0 0 #define B1 1 This clashes with names of the template variables in conjuction * Renaming them to "B" rather than "B1" Now it can be used as-is in Arduino projects * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Daniel Ansorregui Co-authored-by: Niels Lohmann --- include/nlohmann/detail/meta/type_traits.hpp | 8 ++++---- single_include/nlohmann/json.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 984ca1931..0a320e6e2 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -152,10 +152,10 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; -template -struct conjunction -: std::conditional, B1>::type {}; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional, B>::type {}; // https://en.cppreference.com/w/cpp/types/negation template struct negation : std::integral_constant < bool, !B::value > { }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 71f6814ad..ba272a126 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3510,10 +3510,10 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; -template -struct conjunction -: std::conditional, B1>::type {}; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional, B>::type {}; // https://en.cppreference.com/w/cpp/types/negation template struct negation : std::integral_constant < bool, !B::value > { }; From 33b75e513a8f51dd54276e2b56834066c6e36df3 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Mon, 4 Apr 2022 13:18:00 +0200 Subject: [PATCH 033/110] CI: windows-2016 has been deprecated; remove jobs (#3416) --- .github/workflows/windows.yml | 35 ----------------------------------- README.md | 1 - 2 files changed, 36 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e8ca95d51..ade692069 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -29,41 +29,6 @@ jobs: - name: test run: cd build ; ctest -j 10 -C Debug --output-on-failure - msvc2017: - runs-on: windows-2016 - strategy: - matrix: - build_type: [Debug, Release] - architecture: [Win32, x64] - - steps: - - uses: actions/checkout@v2 - - name: cmake - run: cmake -S . -B build -G "Visual Studio 15 2017" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Release' && matrix.architecture == 'x64' - - name: cmake - run: cmake -S . -B build -G "Visual Studio 15 2017" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Release' && matrix.architecture != 'x64' - - name: cmake - run: cmake -S . -B build -G "Visual Studio 15 2017" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DJSON_FastTests=ON -DCMAKE_CXX_FLAGS="/W4 /WX" - if: matrix.build_type == 'Debug' - - name: build - run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 - - name: test - run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure - - msvc2017_latest: - runs-on: windows-2016 - - steps: - - uses: actions/checkout@v2 - - name: cmake - run: cmake -S . -B build -G "Visual Studio 15 2017" -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" - - name: build - run: cmake --build build --config Release --parallel 10 - - name: test - run: cd build ; ctest -j 10 -C Release --output-on-failure - msvc2019: runs-on: windows-2019 strategy: diff --git a/README.md b/README.md index c6babb64e..47e3ab539 100644 --- a/README.md +++ b/README.md @@ -1117,7 +1117,6 @@ The following compilers are currently used in continuous integration at [AppVeyo | NVCC 11.0.221 | Ubuntu 20.04.3 LTS | GitHub Actions | | Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | -| Visual Studio 15 2017 MSVC 19.16.27045.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | GitHub Actions | | Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | GitHub Actions | | Visual Studio 16 2019 MSVC 19.28.29912.0 (Build Engine version 16.9.0+57a23d249 for .NET Framework) | Windows-10.0.17763 | AppVeyor | | Visual Studio 17 2022 MSVC 19.30.30709.0 (Build Engine version 17.0.31804.368 for .NET Framework) | Windows-10.0.20348 | GitHub Actions | From ab5cecb34feb18f5a030517547b0ead1148d05fc Mon Sep 17 00:00:00 2001 From: flagarde Date: Tue, 5 Apr 2022 02:45:19 +0800 Subject: [PATCH 034/110] Report the right __cplusplus value for MSVC in basic_json meta() (#3417) * Report the right __cplusplus value for MSVC in basic_json meta() * Run amalgamate --- include/nlohmann/json.hpp | 5 ++++- single_include/nlohmann/json.hpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 6bf77d507..496007c0d 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -310,7 +310,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; #endif -#ifdef __cplusplus + +#if defined(_MSVC_LANG) + result["compiler"]["c++"] = std::to_string(_MSVC_LANG); +#elif defined(__cplusplus) result["compiler"]["c++"] = std::to_string(__cplusplus); #else result["compiler"]["c++"] = "unknown"; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ba272a126..33a91164c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17505,7 +17505,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; #endif -#ifdef __cplusplus + +#if defined(_MSVC_LANG) + result["compiler"]["c++"] = std::to_string(_MSVC_LANG); +#elif defined(__cplusplus) result["compiler"]["c++"] = std::to_string(__cplusplus); #else result["compiler"]["c++"] = "unknown"; From c2054b96b96ff2045b67e90fbdf19a87be27076c Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Wed, 6 Apr 2022 08:25:35 +0200 Subject: [PATCH 035/110] Restore disabled check for #3070 (except on MSVC) (#3421) Restore the previously disabled check for regression #3070 on all compilers but MSVC. To summarize the issue: Given namespace fs = std::filesystem. On MSVC attempting to construct an fs::path from json results in an ambiguous overload resolution because fs::path can be constructed from both a std::string as well as another fs::path. To the best of my knowledge there is no way to fix an ambiguous overload situation involving a type we do not control and with json implicitly converting to both std::string and fs::path. Re-enabling the check where it compiles and keeping it disabled for MSVC is the best we can do. Closes #3377 and #3382. --- include/nlohmann/detail/conversions/from_json.hpp | 3 +-- single_include/nlohmann/json.hpp | 3 +-- test/src/unit-regression2.cpp | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index fbb176879..207d3e302 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -464,8 +464,7 @@ void from_json(const BasicJsonType& j, std_fs::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - // Not tested because of #3377 (related #3070) - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); // LCOV_EXCL_LINE + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } p = *j.template get_ptr(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 33a91164c..3b694e569 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4279,8 +4279,7 @@ void from_json(const BasicJsonType& j, std_fs::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - // Not tested because of #3377 (related #3070) - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); // LCOV_EXCL_LINE + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); } p = *j.template get_ptr(); } diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index b8cc5107b..dd2edb4ff 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -748,8 +748,10 @@ TEST_CASE("regression tests 2") const auto j_path = j.get(); CHECK(j_path == text_path); - // Disabled pending resolution of #3377 - // CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); +#ifndef _MSC_VER + // works everywhere but on MSVC + CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); +#endif } #endif From fa91409a7341e2c6bbacde8a3f43e58ef65ccc9c Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Wed, 6 Apr 2022 08:26:49 +0200 Subject: [PATCH 036/110] CI: speedup AppVeyor builds by ~30% (#3422) AppVeyor build VMs should provide at least two cores. Parallelize builds and tests. In addition, enable output on failure and disable verbose test output. --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b04ac24f2..0a7de686d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -66,15 +66,15 @@ before_build: - cmake . -G "%GENERATOR%" -A "%GENERATOR_PLATFORM%" -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DJSON_BuildTests=On "%CMAKE_OPTIONS%" build_script: - - cmake --build . --config "%configuration%" + - cmake --build . --config "%configuration%" --parallel 2 test_script: - - if "%configuration%"=="Release" ctest -C "%configuration%" -V -j + - if "%configuration%"=="Release" ctest -C "%configuration%" --parallel 2 --output-on-failure # On Debug builds, skip test-unicode_all # as it is extremely slow to run and cause # occasional timeouts on AppVeyor. # More info: https://github.com/nlohmann/json/pull/1570 - - if "%configuration%"=="Debug" ctest --exclude-regex "test-unicode" -C "%configuration%" -V -j + - if "%configuration%"=="Debug" ctest --exclude-regex "test-unicode" -C "%configuration%" --parallel 2 --output-on-failure # only build PRs and commits to develop branch # (see https://help.appveyor.com/discussions/questions/55079-two-builds-per-commit-to-pull-request) From 52e16a9f9c540a2de963a7b847e3f1611c71567f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 6 Apr 2022 08:29:01 +0200 Subject: [PATCH 037/110] :children_crossing: add check if different version is also included #3337 (#3418) --- doc/mkdocs/docs/features/macros.md | 13 +++++++++++++ include/nlohmann/json.hpp | 8 ++++++++ single_include/nlohmann/json.hpp | 8 ++++++++ third_party/amalgamate/amalgamate.py | 2 +- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index 9994c5dcd..dabb29917 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -125,6 +125,19 @@ security reasons (e.g., Intel Software Guard Extensions (SGX)). #undef JSON_NO_IO ``` +## `JSON_SKIP_LIBRARY_VERSION_CHECK` + +When defined, the library will not create a compiler warning when a different version of the library was already +included. + +!!! info "Default behavior" + + By default, the macro is not defined. + + ```cpp + #undef JSON_SKIP_LIBRARY_VERSION_CHECK + ``` + ## `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 diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 496007c0d..0c3c39065 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -39,6 +39,14 @@ SOFTWARE. #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 10 || NLOHMANN_JSON_VERSION_PATCH != 5 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + #define NLOHMANN_JSON_VERSION_MAJOR 3 #define NLOHMANN_JSON_VERSION_MINOR 10 #define NLOHMANN_JSON_VERSION_PATCH 5 diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3b694e569..ed2a9cf2c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -39,6 +39,14 @@ SOFTWARE. #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 10 || NLOHMANN_JSON_VERSION_PATCH != 5 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + #define NLOHMANN_JSON_VERSION_MAJOR 3 #define NLOHMANN_JSON_VERSION_MINOR 10 #define NLOHMANN_JSON_VERSION_PATCH 5 diff --git a/third_party/amalgamate/amalgamate.py b/third_party/amalgamate/amalgamate.py index f01b43b76..0aed7c901 100755 --- a/third_party/amalgamate/amalgamate.py +++ b/third_party/amalgamate/amalgamate.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # coding=utf-8 # amalgamate.py - Amalgamate C source and header files. From 448b173ccf11dc38752b19640653b262caf60c0a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 6 Apr 2022 15:14:14 +0200 Subject: [PATCH 038/110] Update CI image (#3420) * :arrow_up: use new CI image * :arrow_up: use new CI image * :rotating_light: suppress Clang-Tidy warnings * :green_heart: use proper scan-build version * :rotating_light: suppress Clang-Tidy warnings * :arrow_up: use more recent GitHub actions * :green_heart: add missing compiler * :memo: update used compilers * :rotating_light: fix duplicate inclusion --- .clang-tidy | 1 + .github/workflows/codeql-analysis.yml | 10 ++++---- .github/workflows/macos.yml | 4 +-- .github/workflows/ubuntu.yml | 36 +++++++++++++-------------- .github/workflows/windows.yml | 14 +++++------ README.md | 9 ++++--- cmake/ci.cmake | 8 +++--- include/nlohmann/json.hpp | 8 +++--- single_include/nlohmann/json.hpp | 8 +++--- test/src/unit-udt.cpp | 1 - 10 files changed, 50 insertions(+), 49 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 30886d7c8..3673930d4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,6 +4,7 @@ Checks: '*, -altera-unroll-loops, -android-cloexec-fopen, -bugprone-easily-swappable-parameters, + -cert-err58-cpp, -concurrency-mt-unsafe, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-magic-numbers, diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 01b333497..af35f4f26 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,10 +18,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. + # a pull request then we can check out the head. fetch-depth: 2 # If this run was triggered by a pull request event, then checkout @@ -31,7 +31,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -39,7 +39,7 @@ jobs: # 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 + uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -53,4 +53,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index d01502bf5..03c652a81 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -19,7 +19,7 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_FastTests=ON - name: build @@ -36,7 +36,7 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DJSON_TestStandards=${{ matrix.standard }} - name: build diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 634545a4f..b5f583367 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -12,9 +12,9 @@ on: jobs: ci_test_clang: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build @@ -22,9 +22,9 @@ jobs: ci_test_gcc: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build @@ -32,12 +32,12 @@ jobs: ci_static_analysis: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 strategy: matrix: target: [ci_clang_tidy, ci_cppcheck, ci_test_valgrind, ci_test_clang_sanitizer, ci_test_amalgamation, ci_clang_analyze, ci_cpplint, ci_cmake_flags, ci_single_binaries, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_infer] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build @@ -45,12 +45,12 @@ jobs: ci_cmake_options: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 strategy: matrix: target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build @@ -58,15 +58,15 @@ jobs: ci_test_coverage: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build run: cmake --build build --target ci_test_coverage - name: archive coverage report - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: code-coverage-report path: /__w/json/json/build/html @@ -78,12 +78,12 @@ jobs: ci_test_compilers: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 strategy: matrix: - compiler: [g++-4.8, g++-4.9, g++-5, g++-6, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11, clang++-12, clang++-13] + compiler: [g++-4.8, g++-4.9, g++-5, g++-6, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11, clang++-12, clang++-13, clang++-14] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build @@ -91,13 +91,13 @@ jobs: ci_test_standards: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 strategy: matrix: standard: [11, 14, 17, 20] compiler: [gcc, clang] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build @@ -105,9 +105,9 @@ jobs: ci_cuda_example: runs-on: ubuntu-latest - container: ghcr.io/nlohmann/json-ci:v2.2.0 + container: ghcr.io/nlohmann/json-ci:v2.3.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -DJSON_CI=On - name: build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index ade692069..8d3e54358 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -17,7 +17,7 @@ jobs: architecture: [x64, x86] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up MinGW uses: egor-tensin/setup-mingw@v2 with: @@ -37,7 +37,7 @@ jobs: architecture: [Win32, x64] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" if: matrix.build_type == 'Release' @@ -53,7 +53,7 @@ jobs: runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -G "Visual Studio 16 2019" -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" - name: build @@ -69,7 +69,7 @@ jobs: architecture: [Win32, x64] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX" if: matrix.build_type == 'Release' @@ -85,7 +85,7 @@ jobs: runs-on: windows-2022 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX" - name: build @@ -100,7 +100,7 @@ jobs: version: [11, 12] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: install Clang run: curl -fsSL -o LLVM${{ matrix.version }}.exe https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.version }}.0.0/LLVM-${{ matrix.version }}.0.0-win64.exe ; 7z x LLVM${{ matrix.version }}.exe -y -o"C:/Program Files/LLVM" - name: cmake @@ -117,7 +117,7 @@ jobs: architecture: [Win32, x64] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: cmake run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -T ClangCL -DJSON_BuildTests=On - name: build diff --git a/README.md b/README.md index 47e3ab539..9d9bd2c91 100644 --- a/README.md +++ b/README.md @@ -1037,9 +1037,9 @@ auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE Though it's 2022 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: -- GCC 4.8 - 11.0 (and possibly later) -- Clang 3.4 - 13.0 (and possibly later) -- Apple Clang 9.1 - 13.0 (and possibly later) +- GCC 4.8 - 12.0 (and possibly later) +- Clang 3.4 - 15.0 (and possibly later) +- Apple Clang 9.1 - 13.1 (and possibly later) - Intel C++ Compiler 17.0.2 (and possibly later) - Nvidia CUDA Compiler 11.0.221 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) @@ -1093,8 +1093,8 @@ The following compilers are currently used in continuous integration at [AppVeyo | GCC 8.4.0 (Ubuntu 8.4.0-3ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions | | GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) | Ubuntu 20.04.3 LTS | GitHub Actions | | GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) | Ubuntu 20.04.3 LTS | GitHub Actions | -| GCC 11.0.1 20210321 (experimental) | Ubuntu 20.04.3 LTS | GitHub Actions | | GCC 11.1.0 | Ubuntu (aarch64) | Drone CI | +| GCC 12.0.0 20211219 (experimental) | Ubuntu 20.04.3 LTS | GitHub Actions | | Clang 3.5.2 (3.5.2-3ubuntu1) | Ubuntu 20.04.3 LTS | GitHub Actions | | Clang 3.6.2 (3.6.2-3ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions | | Clang 3.7.1 (3.7.1-2ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions | @@ -1114,6 +1114,7 @@ The following compilers are currently used in continuous integration at [AppVeyo | Clang 12.0.0 (12.0.0-3ubuntu1~20.04.3) | Ubuntu 20.04.3 LTS | GitHub Actions | | Clang 13.0.1 (13.0.1-++20211015123032+cf15ccdeb6d5-1exp120211015003613.5) | Ubuntu 20.04.3 LTS | GitHub Actions | | Clang 14.0.0 (14.0.0-++20211221052852+55c71c9eac9b-1exp120211221172954.95) | Ubuntu 20.04.3 LTS | GitHub Actions | +| Clang 15.0.0 (15.0.0-++20220403052648+896770c9a92e-1~exp1~20220403172744.209) | Ubuntu 20.04.3 LTS | GitHub Actions | | NVCC 11.0.221 | Ubuntu 20.04.3 LTS | GitHub Actions | | Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | diff --git a/cmake/ci.cmake b/cmake/ci.cmake index b9078b625..be564fa99 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -13,12 +13,12 @@ execute_process(COMMAND ${ASTYLE_TOOL} --version OUTPUT_VARIABLE ASTYLE_TOOL_VER string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" ASTYLE_TOOL_VERSION "${ASTYLE_TOOL_VERSION}") message(STATUS "🔖 Artistic Style ${ASTYLE_TOOL_VERSION} (${ASTYLE_TOOL})") -find_program(CLANG_TOOL NAMES clang++-HEAD clang++-14 clang++-13 clang++-12 clang++-11 clang++) +find_program(CLANG_TOOL NAMES clang++-HEAD clang++-15 clang++-14 clang++-13 clang++-12 clang++-11 clang++) execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}") message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})") -find_program(CLANG_TIDY_TOOL NAMES clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy) +find_program(CLANG_TIDY_TOOL NAMES clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy) execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}") message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})") @@ -79,7 +79,7 @@ message(STATUS "🔖 Valgrind ${VALGRIND_TOOL_VERSION} (${VALGRIND_TOOL})") find_program(GENHTML_TOOL NAMES genhtml) find_program(PLOG_CONVERTER_TOOL NAMES plog-converter) find_program(PVS_STUDIO_ANALYZER_TOOL NAMES pvs-studio-analyzer) -find_program(SCAN_BUILD_TOOL NAMES scan-build-14 scan-build-13 scan-build-12 scan-build-11 scan-build) +find_program(SCAN_BUILD_TOOL NAMES scan-build-15 scan-build-14 scan-build-13 scan-build-12 scan-build-11 scan-build) # the individual source files file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp) @@ -841,7 +841,7 @@ add_custom_target(ci_cmake_flags # Use more installed compilers. ############################################################################### -foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13) +foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14) find_program(COMPILER_TOOL NAMES ${COMPILER}) if (COMPILER_TOOL) if ("${COMPILER}" STREQUAL "clang++-9") diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 0c3c39065..db85f8d7c 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -47,9 +47,9 @@ SOFTWARE. #endif #endif -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 10 -#define NLOHMANN_JSON_VERSION_PATCH 5 +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 10 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 5 // NOLINT(modernize-macro-to-enum) #include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t @@ -827,7 +827,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief create a null object /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ - basic_json(std::nullptr_t = nullptr) noexcept + basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape) : basic_json(value_t::null) { assert_invariant(); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ed2a9cf2c..8e2863501 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -47,9 +47,9 @@ SOFTWARE. #endif #endif -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 10 -#define NLOHMANN_JSON_VERSION_PATCH 5 +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 10 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 5 // NOLINT(modernize-macro-to-enum) #include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t @@ -18021,7 +18021,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief create a null object /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ - basic_json(std::nullptr_t = nullptr) noexcept + basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape) : basic_json(value_t::null) { assert_invariant(); diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 0bc324122..a3f06210d 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -39,7 +39,6 @@ using nlohmann::json; #include #include #include -#include #include namespace udt From 15fa6a342af7b51cb51a22599026e01f1d81957b Mon Sep 17 00:00:00 2001 From: Michael Nosthoff Date: Wed, 6 Apr 2022 15:26:35 +0200 Subject: [PATCH 039/110] at.md: fix typo (#3426) --- doc/mkdocs/docs/api/basic_json/at.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/api/basic_json/at.md b/doc/mkdocs/docs/api/basic_json/at.md index 1ad661322..fc518f46e 100644 --- a/doc/mkdocs/docs/api/basic_json/at.md +++ b/doc/mkdocs/docs/api/basic_json/at.md @@ -24,7 +24,7 @@ const_reference at(const json_pointer& ptr) const; : index of the element to access `key` (in) -: object key of the elements to remove +: object key of the elements to access `ptr` (in) : JSON pointer to the desired element From 261cc4e509c51d53c57d0c266abd4a78f134e6a4 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 8 Apr 2022 10:22:47 +0200 Subject: [PATCH 040/110] Fix constraints on from_json() for strings (#3427) Constrain from_json() overload for StringType to not accept json_ref and require it to be assignable, instead of constructible, from basic_json::string_t. Re-enable C++14 tests on Clang <4.0. Fixes #3171. Fixes #3267. Fixes #3312. Fixes #3384. --- .../nlohmann/detail/conversions/from_json.hpp | 11 ++- single_include/nlohmann/json.hpp | 11 ++- test/CMakeLists.txt | 5 -- test/src/unit-regression2.cpp | 69 +++++++++++++++++++ 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 207d3e302..cc06f198b 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -105,13 +105,12 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) } template < - typename BasicJsonType, typename ConstructibleStringType, + typename BasicJsonType, typename StringType, enable_if_t < - is_constructible_string_type::value&& - !std::is_same::value, - int > = 0 > -void from_json(const BasicJsonType& j, ConstructibleStringType& s) + std::is_assignable::value + && !std::is_same::value + && !is_json_ref::value, int > = 0 > +void from_json(const BasicJsonType& j, StringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8e2863501..c1f545b07 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3928,13 +3928,12 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) } template < - typename BasicJsonType, typename ConstructibleStringType, + typename BasicJsonType, typename StringType, enable_if_t < - is_constructible_string_type::value&& - !std::is_same::value, - int > = 0 > -void from_json(const BasicJsonType& j, ConstructibleStringType& s) + std::is_assignable::value + && !std::is_same::value + && !is_json_ref::value, int > = 0 > +void from_json(const BasicJsonType& j, StringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 90c325861..2f06def50 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,11 +11,6 @@ include(test) # override standard support ############################################################################# -# compiling json.hpp in C++14 mode fails with Clang <4.0 -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0) - unset(compiler_supports_cpp_14) -endif() - # Clang only supports C++17 starting from Clang 5.0 if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) unset(compiler_supports_cpp_17) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index dd2edb4ff..55bbcac65 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -240,6 +240,50 @@ inline void from_json(const nlohmann::json& j, FooBar& fb) j.at("value").get_to(fb.foo.value); } +///////////////////////////////////////////////////////////////////// +// for #3171 +///////////////////////////////////////////////////////////////////// + +struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions) +{ + for_3171_base(const std::string& /*unused*/ = {}) {} + virtual ~for_3171_base() = default; + + virtual void _from_json(const json& j) + { + j.at("str").get_to(str); + } + + std::string str{}; +}; + +struct for_3171_derived : public for_3171_base +{ + for_3171_derived() = default; + explicit for_3171_derived(const std::string& /*unused*/) { } +}; + +inline void from_json(const json& j, for_3171_base& tb) +{ + tb._from_json(j); +} + +///////////////////////////////////////////////////////////////////// +// for #3312 +///////////////////////////////////////////////////////////////////// + +#ifdef JSON_HAS_CPP_20 +struct for_3312 +{ + std::string name; +}; + +inline void from_json(const json& j, for_3312& obj) +{ + j.at("name").get_to(obj.name); +} +#endif + TEST_CASE("regression tests 2") { SECTION("issue #1001 - Fix memory leak during parser callback") @@ -791,6 +835,31 @@ TEST_CASE("regression tests 2") CHECK(jit->first == ojit->first); CHECK(jit->second.get() == ojit->second.get()); } + + SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed") + { + json j{{ "str", "value"}}; + + // failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’ + // {aka ‘const std::__cxx11::basic_string’}) + // s = *j.template get_ptr(); + auto td = j.get(); + + CHECK(td.str == "value"); + } + +#ifdef JSON_HAS_CPP_20 + SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20") + { + // see test for #3171 + ordered_json j = {{"name", "class"}}; + for_3312 obj{}; + + j.get_to(obj); + + CHECK(obj.name == "class"); + } +#endif } DOCTEST_CLANG_SUPPRESS_WARNING_POP From 10344907ffa10189c52a7ddc8e1dab13c886686a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 9 Apr 2022 18:09:23 +0200 Subject: [PATCH 041/110] :memo: document which version introduced the macros #3429 (#3431) --- doc/mkdocs/docs/api/macros/index.md | 4 +++ doc/mkdocs/docs/features/macros.md | 46 +++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/doc/mkdocs/docs/api/macros/index.md b/doc/mkdocs/docs/api/macros/index.md index 9c390d55e..4c723c6a6 100644 --- a/doc/mkdocs/docs/api/macros/index.md +++ b/doc/mkdocs/docs/api/macros/index.md @@ -1,5 +1,9 @@ # Macros +!!! note + + This page is under construction. See the [macro overview page](../../features/macros.md) until then. + Some aspects of the library can be configured by defining preprocessor macros before including the `json.hpp` header. - [`JSON_ASSERT(x)`](json_assert.md) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index dabb29917..576ec935d 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -14,6 +14,8 @@ This macro controls which code is executed for runtime assertions of the librari #define JSON_ASSERT(x) assert(x) ``` +The macro was introduced in version 3.9.0. + ## `JSON_CATCH_USER(exception)` This macro overrides [`#!cpp catch`](https://en.cppreference.com/w/cpp/language/try_catch) calls inside the library. @@ -39,6 +41,8 @@ See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an #define JSON_CATCH_USER(exception) if (false) ``` +The macro was introduced in version 3.1.0. + ## `JSON_DIAGNOSTICS` This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable @@ -63,6 +67,8 @@ The diagnostics messages can also be controlled with the CMake option `JSON_Diag #define JSON_DIAGNOSTICS 0 ``` +The macro was introduced in version 3.10.0. + ## `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, `JSON_HAS_CPP_20` The library targets C++11, but also supports some features introduced in later C++ versions (e.g., `std::string_view` @@ -76,6 +82,8 @@ detected incorrectly. The default value is detected based on the preprocessor macros `#!cpp __cplusplus`, `#!cpp _HAS_CXX17`, or `#!cpp _MSVC_LANG`. +The macros were introduced in version 3.10.5. + ## `JSON_HAS_FILESYSTEM`, `JSON_HAS_EXPERIMENTAL_FILESYSTEM` When compiling with C++17, the library provides conversions from and to `std::filesystem::path`. As compiler support @@ -92,6 +100,8 @@ To override the built-in check, define `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERI Note that older compilers or older versions of libstd++ also require the library `stdc++fs` to be linked to for filesystem support. +The macros were introduced in version 3.10.5. + ## `JSON_NOEXCEPTION` Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. When defining `JSON_NOEXCEPTION`, `#!cpp try` @@ -111,6 +121,8 @@ The same effect is achieved by setting the compiler flag `-fno-exceptions`. Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). +The macro was introduced in version 2.1.0. + ## `JSON_NO_IO` When defined, headers ``, ``, ``, ``, and `` are not included and parse functions @@ -125,6 +137,8 @@ security reasons (e.g., Intel Software Guard Extensions (SGX)). #undef JSON_NO_IO ``` +The macro was introduced in version 3.10.0. + ## `JSON_SKIP_LIBRARY_VERSION_CHECK` When defined, the library will not create a compiler warning when a different version of the library was already @@ -138,6 +152,8 @@ included. #undef JSON_SKIP_LIBRARY_VERSION_CHECK ``` +The macro was introduced in version 3.11.0. + ## `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 @@ -151,6 +167,8 @@ use the library with compilers that do not fully support C++11 and may only work #undef JSON_SKIP_UNSUPPORTED_COMPILER_CHECK ``` +The macro was introduced in version 3.2.0. + ## `JSON_THROW_USER(exception)` This macro overrides `#!cpp throw` calls inside the library. The argument is the exception to be thrown. Note that @@ -176,6 +194,8 @@ undefined behavior. See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example. +The macro was introduced in version 3.1.0. + ## `JSON_TRY_USER` This macro overrides `#!cpp try` calls inside the library. It has no arguments and is always followed by a scope. @@ -198,6 +218,8 @@ This macro overrides `#!cpp try` calls inside the library. It has no arguments a See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example. +The macro was introduced in version 3.1.0. + ## `JSON_USE_IMPLICIT_CONVERSIONS` When defined to `0`, implicit conversions are switched off. By default, implicit conversions are switched on. @@ -228,6 +250,8 @@ sets `JSON_USE_IMPLICIT_CONVERSIONS` accordingly. #define JSON_USE_IMPLICIT_CONVERSIONS 1 ``` +The macro was introduced in version 3.9.0. + ## `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 @@ -239,9 +263,16 @@ The first parameter is the name of the class/struct, and all remaining parameter See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example. +The macro was introduced in version 3.9.0. + ## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)` -This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the type be default constructible. The `from_json()` function default constructs an object and uses its values as the defaults when calling the `value()` function. +This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. It will not throw an exception in `from_json()` due to a +missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the +type be default constructible. The `from_json()` function default constructs an object and uses its values as the +defaults when calling the `value()` function. + +The macro was introduced in version 3.11.0. ## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)` @@ -254,16 +285,27 @@ first parameter is the name of the class/struct, and all remaining parameters na See [Simplify your life with macros](arbitrary_types.md#simplify-your-life-with-macros) for an example. +The macro was introduced in version 3.9.0. + ## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)` -This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the type be default constructible. The `from_json()` function default constructs an object and uses its values as the defaults when calling the `value()` function. +This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. It will not throw an exception in `from_json()` due to a +missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the +type be default constructible. The `from_json()` function default constructs an object and uses its values as the +defaults when calling the `value()` function. + +The macro was introduced in version 3.11.0. ## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` This macro simplifies the serialization/deserialization of enum types. See [Specializing enum conversion](enum_conversion.md) for more information. +The macro was introduced in version 3.4.0. + ## `NLOHMANN_JSON_VERSION_MAJOR`, `NLOHMANN_JSON_VERSION_MINOR`, `NLOHMANN_JSON_VERSION_PATCH` These macros are defined by the library and contain the version numbers according to [Semantic Versioning 2.0.0](https://semver.org). + +The macros were introduced in version 3.1.0. From 1deeb434c679e7908875ce4ce0ce4583071ecc74 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Tue, 12 Apr 2022 14:08:20 +0200 Subject: [PATCH 042/110] Exclude std::any from implicit conversion (fixes #3428) (#3437) * Exclude std::any from implicit conversion Fixes #3428 (MSVC) and silences compiler warning on GCC (-Wconversion). * Exclude std::any from implicit conversion --- include/nlohmann/json.hpp | 4 ++++ single_include/nlohmann/json.hpp | 4 ++++ test/src/unit-regression2.cpp | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index db85f8d7c..b77670803 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -94,6 +94,7 @@ SOFTWARE. #include #if defined(JSON_HAS_CPP_17) + #include #include #endif @@ -1891,6 +1892,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) detail::negation>, +#endif +#if defined(JSON_HAS_CPP_17) + detail::negation>, #endif detail::is_detected_lazy >::value, int >::type = 0 > diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c1f545b07..53c15b651 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17287,6 +17287,7 @@ template , #if defined(JSON_HAS_CPP_17) + #include #include #endif @@ -19084,6 +19085,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) detail::negation>, +#endif +#if defined(JSON_HAS_CPP_17) + detail::negation>, #endif detail::is_detected_lazy >::value, int >::type = 0 > diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 55bbcac65..feb800e76 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -43,6 +43,7 @@ using ordered_json = nlohmann::ordered_json; #include #ifdef JSON_HAS_CPP_17 + #include #include #endif @@ -860,6 +861,18 @@ TEST_CASE("regression tests 2") CHECK(obj.name == "class"); } #endif + +#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS + SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any") + { + json j; + std::any a1 = j; + std::any&& a2 = j; + + CHECK(a1.type() == typeid(j)); + CHECK(a2.type() == typeid(j)); + } +#endif } DOCTEST_CLANG_SUPPRESS_WARNING_POP From 616caea27af15a14b83b877b8ce0cde328b9d10a Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Tue, 12 Apr 2022 14:18:16 +0200 Subject: [PATCH 043/110] Re-template json_pointer on string type (#3415) * Make exception context optional Change exception context parameter to pointer and replace context with nullptr where appropriate. * Support escaping other string types * Add string concatenation function Add variadic concat() function for concatenating char *, char, and string types. * Replace string concatenations using + with concat() * Template json_pointer on string type Change json_pointer from being templated on basic_json to being templated on string type. * Add unit test for #3388 Closes #3388. * Fix regression test for #2958 * Add backwards compatibility with json_pointer * Update json_pointer docs * Allow comparing different json_pointers * Update version numbers --- doc/mkdocs/docs/api/json_pointer/back.md | 5 +- doc/mkdocs/docs/api/json_pointer/index.md | 19 +- .../docs/api/json_pointer/json_pointer.md | 5 +- .../docs/api/json_pointer/operator_slash.md | 4 +- .../docs/api/json_pointer/operator_slasheq.md | 4 +- .../docs/api/json_pointer/operator_string.md | 9 +- doc/mkdocs/docs/api/json_pointer/push_back.md | 7 +- doc/mkdocs/docs/api/json_pointer/string_t.md | 12 + doc/mkdocs/docs/api/json_pointer/to_string.md | 5 +- doc/mkdocs/mkdocs.yml | 1 + .../nlohmann/detail/conversions/from_json.hpp | 37 +- include/nlohmann/detail/exceptions.hpp | 71 +- .../nlohmann/detail/input/binary_reader.hpp | 58 +- include/nlohmann/detail/input/json_sax.hpp | 9 +- include/nlohmann/detail/input/parser.hpp | 35 +- .../nlohmann/detail/iterators/iter_impl.hpp | 24 +- include/nlohmann/detail/json_pointer.hpp | 202 ++- include/nlohmann/detail/meta/type_traits.hpp | 15 + .../nlohmann/detail/output/binary_writer.hpp | 7 +- include/nlohmann/detail/output/serializer.hpp | 5 +- include/nlohmann/detail/string_concat.hpp | 139 ++ include/nlohmann/detail/string_escape.hpp | 22 +- include/nlohmann/json.hpp | 227 +-- single_include/nlohmann/json.hpp | 1284 ++++++++++------- test/src/unit-alt-string.cpp | 47 +- test/src/unit-convenience.cpp | 113 ++ test/src/unit-json_pointer.cpp | 50 + test/src/unit-regression2.cpp | 6 +- 28 files changed, 1638 insertions(+), 784 deletions(-) create mode 100644 doc/mkdocs/docs/api/json_pointer/string_t.md create mode 100644 include/nlohmann/detail/string_concat.hpp diff --git a/doc/mkdocs/docs/api/json_pointer/back.md b/doc/mkdocs/docs/api/json_pointer/back.md index 2b267bc58..240bc6e1e 100644 --- a/doc/mkdocs/docs/api/json_pointer/back.md +++ b/doc/mkdocs/docs/api/json_pointer/back.md @@ -1,7 +1,7 @@ # nlohmann::json_pointer::back ```cpp -const std::string& back() const; +const string_t& back() const; ``` Return last reference token. @@ -36,4 +36,5 @@ Constant. ## Version history -Added in version 3.6.0. +- Added in version 3.6.0. +- Changed return type to `string_t` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/json_pointer/index.md b/doc/mkdocs/docs/api/json_pointer/index.md index d95ed0ffb..3504c9ff7 100644 --- a/doc/mkdocs/docs/api/json_pointer/index.md +++ b/doc/mkdocs/docs/api/json_pointer/index.md @@ -1,7 +1,7 @@ # nlohmann::json_pointer ```cpp -template +template class json_pointer; ``` @@ -11,14 +11,18 @@ are the base for JSON patches. ## Template parameters -`BasicJsonType` -: a specialization of [`basic_json`](../basic_json/index.md) +`RefStringType` +: the string type used for the reference tokens making up the JSON pointer + +## Notes + +For backwards compatibility `RefStringType` may also be a specialization of [`basic_json`](../basic_json/index.md) in which case `string_t` will be deduced as [`basic_json::string_t`](../basic_json/string_t.md). This feature is deprecated and may be removed in a future major version. ## Member functions - [(constructor)](json_pointer.md) - [**to_string**](to_string.md) - return a string representation of the JSON pointer -- [**operator std::string**](operator_string.md) - return a string representation of the JSON pointer +- [**operator string_t**](operator_string.md) - return a string representation of the JSON pointer - [**operator/=**](operator_slasheq.md) - append to the end of the JSON pointer - [**operator/**](operator_slash.md) - create JSON Pointer by appending - [**parent_pointer**](parent_pointer.md) - returns the parent of this JSON pointer @@ -27,6 +31,10 @@ are the base for JSON patches. - [**push_back**](push_back.md) - append an unescaped token at the end of the pointer - [**empty**](empty.md) - return whether pointer points to the root document +## Member types + +- [**string_t**](string_t.md) - the string type used for the reference tokens + ## See also - [operator""_json_pointer](../basic_json/operator_literal_json_pointer.md) - user-defined string literal for JSON pointers @@ -34,4 +42,5 @@ are the base for JSON patches. ## Version history -Added in version 2.0.0. +- Added in version 2.0.0. +- Changed template parameter from `basic_json` to string type in version 3.11.0. diff --git a/doc/mkdocs/docs/api/json_pointer/json_pointer.md b/doc/mkdocs/docs/api/json_pointer/json_pointer.md index 1e68a28f3..5e7057fc9 100644 --- a/doc/mkdocs/docs/api/json_pointer/json_pointer.md +++ b/doc/mkdocs/docs/api/json_pointer/json_pointer.md @@ -1,7 +1,7 @@ # nlohmann::json_pointer::json_pointer ```cpp -explicit json_pointer(const std::string& s = ""); +explicit json_pointer(const string_t& s = ""); ``` Create a JSON pointer according to the syntax described in @@ -37,4 +37,5 @@ Create a JSON pointer according to the syntax described in ## Version history -Added in version 2.0.0. +- Added in version 2.0.0. +- Changed type of `s` to `string_t` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/json_pointer/operator_slash.md b/doc/mkdocs/docs/api/json_pointer/operator_slash.md index c92875435..ed77b504b 100644 --- a/doc/mkdocs/docs/api/json_pointer/operator_slash.md +++ b/doc/mkdocs/docs/api/json_pointer/operator_slash.md @@ -5,7 +5,7 @@ json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs); // (2) -json_pointer operator/(const json_pointer& lhs, std::string token); +json_pointer operator/(const json_pointer& lhs, string_t token); // (3) json_pointer operator/(const json_pointer& lhs, std::size_t array_idx); @@ -60,5 +60,5 @@ json_pointer operator/(const json_pointer& lhs, std::size_t array_idx); ## Version history 1. Added in version 3.6.0. -2. Added in version 3.6.0. +2. Added in version 3.6.0. Changed type of `token` to `string_t` in version 3.11.0. 3. Added in version 3.6.0. diff --git a/doc/mkdocs/docs/api/json_pointer/operator_slasheq.md b/doc/mkdocs/docs/api/json_pointer/operator_slasheq.md index eb6c30861..3518557d5 100644 --- a/doc/mkdocs/docs/api/json_pointer/operator_slasheq.md +++ b/doc/mkdocs/docs/api/json_pointer/operator_slasheq.md @@ -5,7 +5,7 @@ json_pointer& operator/=(const json_pointer& ptr); // (2) -json_pointer& operator/=(std::string token); +json_pointer& operator/=(string_t token); // (3) json_pointer& operator/=(std::size_t array_idx) @@ -57,5 +57,5 @@ json_pointer& operator/=(std::size_t array_idx) ## Version history 1. Added in version 3.6.0. -2. Added in version 3.6.0. +2. Added in version 3.6.0. Changed type of `token` to `string_t` in version 3.11.0. 3. Added in version 3.6.0. diff --git a/doc/mkdocs/docs/api/json_pointer/operator_string.md b/doc/mkdocs/docs/api/json_pointer/operator_string.md index 56d8eebeb..836728c18 100644 --- a/doc/mkdocs/docs/api/json_pointer/operator_string.md +++ b/doc/mkdocs/docs/api/json_pointer/operator_string.md @@ -1,7 +1,7 @@ -# nlohmann::json_pointer::operator std::string +# nlohmann::json_pointer::operator string_t ```cpp -operator std::string() const +operator string_t() const ``` Return a string representation of the JSON pointer. @@ -13,7 +13,7 @@ A string representation of the JSON pointer ## Possible implementation ```cpp -operator std::string() const +operator string_t() const { return to_string(); } @@ -21,4 +21,5 @@ operator std::string() const ## Version history -Since version 2.0.0. +- Since version 2.0.0. +- Changed type to `string_t` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/json_pointer/push_back.md b/doc/mkdocs/docs/api/json_pointer/push_back.md index 3ebcdbc5c..c1c19cb8d 100644 --- a/doc/mkdocs/docs/api/json_pointer/push_back.md +++ b/doc/mkdocs/docs/api/json_pointer/push_back.md @@ -1,9 +1,9 @@ # nlohmann::json_pointer::push_back ```cpp -void push_back(const std::string& token); +void push_back(const string_t& token); -void push_back(std::string&& token); +void push_back(string_t&& token); ``` Append an unescaped token at the end of the reference pointer. @@ -35,4 +35,5 @@ Amortized constant. ## Version history -Added in version 3.6.0. +- Added in version 3.6.0. +- Changed type of `token` to `string_t` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/json_pointer/string_t.md b/doc/mkdocs/docs/api/json_pointer/string_t.md new file mode 100644 index 000000000..f18b51b81 --- /dev/null +++ b/doc/mkdocs/docs/api/json_pointer/string_t.md @@ -0,0 +1,12 @@ +# nlohmann::json_pointer::string_t +```cpp +using string_t = RefStringType; +``` + +The string type used for the reference tokens making up the JSON pointer. + +See [`basic_json::string_t`](../basic_json/string_t.md) for more information. + +## Version history + +- Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/json_pointer/to_string.md b/doc/mkdocs/docs/api/json_pointer/to_string.md index 9287436e3..fae3abe5f 100644 --- a/doc/mkdocs/docs/api/json_pointer/to_string.md +++ b/doc/mkdocs/docs/api/json_pointer/to_string.md @@ -1,7 +1,7 @@ # nlohmann::json_pointer::to_string ```cpp -std::string to_string() const; +string_t to_string() const; ``` Return a string representation of the JSON pointer. @@ -36,4 +36,5 @@ ptr == json_pointer(ptr.to_string()); ## Version history -Since version 2.0.0. +- Since version 2.0.0. +- Changed return type to `string_t` in version 3.11.0. diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 618f7344b..ddd55e5c9 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -211,6 +211,7 @@ nav: - 'parent_pointer': api/json_pointer/parent_pointer.md - 'pop_back': api/json_pointer/pop_back.md - 'push_back': api/json_pointer/push_back.md + - 'string_t': api/json_pointer/string_t.md - 'to_string': api/json_pointer/to_string.md - json_sax: - 'Overview': api/json_sax/index.md diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index cc06f198b..079baa723 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #if JSON_HAS_EXPERIMENTAL_FILESYSTEM @@ -42,7 +43,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j)); } n = nullptr; } @@ -80,7 +81,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); } } @@ -89,7 +90,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j)); } b = *j.template get_ptr(); } @@ -99,7 +100,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } s = *j.template get_ptr(); } @@ -114,7 +115,7 @@ void from_json(const BasicJsonType& j, StringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } s = *j.template get_ptr(); @@ -154,7 +155,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -171,7 +172,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -268,7 +269,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } from_json_array_impl(j, arr, priority_tag<3> {}); @@ -287,7 +288,7 @@ auto from_json(BasicJsonType&& j, identity_tag> tag) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); @@ -298,7 +299,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j)); } bin = *j.template get_ptr(); @@ -310,7 +311,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); } ConstructibleObjectType ret; @@ -370,7 +371,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); } } @@ -411,7 +412,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); @@ -424,14 +425,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j)); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -444,14 +445,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(), p.at(1).template get()); } @@ -463,7 +464,7 @@ void from_json(const BasicJsonType& j, std_fs::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } p = *j.template get_ptr(); } diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index b4b180496..4d61de402 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -1,5 +1,6 @@ #pragma once +#include // nullptr_t #include // exception #include // runtime_error #include // to_string @@ -9,6 +10,10 @@ #include #include #include +#include +#include +#include + namespace nlohmann { @@ -38,15 +43,20 @@ class exception : public std::exception static std::string name(const std::string& ename, int id_) { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + return concat("[json.exception.", ename, '.', std::to_string(id_), "] "); + } + + static std::string diagnostics(std::nullptr_t /*leaf_element*/) + { + return ""; } template - static std::string diagnostics(const BasicJsonType& leaf_element) + static std::string diagnostics(const BasicJsonType* leaf_element) { #if JSON_DIAGNOSTICS std::vector tokens; - for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent) { switch (current->m_parent->type()) { @@ -94,11 +104,12 @@ class exception : public std::exception return ""; } - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) + auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) { - return a + "/" + detail::escape(b); - }) + ") "; + return concat(a, '/', detail::escape(b)); + }); + return concat('(', str, ") "); #else static_cast(leaf_element); return ""; @@ -124,20 +135,20 @@ class parse_error : public exception @param[in] what_arg the explanatory string @return parse_error object */ - template - static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + template::value, int> = 0> + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context) { - std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + std::string w = concat(exception::name("parse_error", id_), "parse error", + position_string(pos), ": ", exception::diagnostics(context), what_arg); return {id_, pos.chars_read_total, w.c_str()}; } - template - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + template::value, int> = 0> + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context) { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + exception::diagnostics(context) + what_arg; + std::string w = concat(exception::name("parse_error", id_), "parse error", + (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""), + ": ", exception::diagnostics(context), what_arg); return {id_, byte_, w.c_str()}; } @@ -158,8 +169,8 @@ class parse_error : public exception static std::string position_string(const position_t& pos) { - return " at line " + std::to_string(pos.lines_read + 1) + - ", column " + std::to_string(pos.chars_read_current_line); + return concat(" at line ", std::to_string(pos.lines_read + 1), + ", column ", std::to_string(pos.chars_read_current_line)); } }; @@ -168,10 +179,10 @@ class parse_error : public exception class invalid_iterator : public exception { public: - template - static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + template::value, int> = 0> + static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context) { - std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -186,10 +197,10 @@ class invalid_iterator : public exception class type_error : public exception { public: - template - static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + template::value, int> = 0> + static type_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -203,10 +214,10 @@ class type_error : public exception class out_of_range : public exception { public: - template - static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + template::value, int> = 0> + static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context) { - std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } @@ -220,10 +231,10 @@ class out_of_range : public exception class other_error : public exception { public: - template - static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + template::value, int> = 0> + static other_error create(int id_, const std::string& what_arg, BasicJsonContext context) { - std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); return {id_, w.c_str()}; } diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 65e0047ac..4041cedba 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace nlohmann @@ -139,8 +140,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { - return sax->parse_error(chars_read, get_token_string(), - parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, + exception_message(format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); } } @@ -216,7 +217,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr)); } return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); @@ -237,7 +239,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 0)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr)); } // All BSON binary values have a subtype @@ -319,7 +322,9 @@ class binary_reader { std::array cr{{}}; static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + std::string cr_str{cr.data()}; + return sax->parse_error(element_type_parse_position, cr_str, + parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr)); } } } @@ -719,7 +724,8 @@ class binary_reader case cbor_tag_handler_t::error: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); } case cbor_tag_handler_t::ignore: @@ -876,7 +882,8 @@ class binary_reader default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); } } } @@ -971,7 +978,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr)); } } } @@ -1070,7 +1078,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr)); } } } @@ -1540,7 +1549,8 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr)); } } } @@ -1622,7 +1632,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr)); } } } @@ -1872,7 +1883,8 @@ class binary_reader default: auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr)); } } @@ -1942,7 +1954,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr)); } } } @@ -1980,7 +1993,8 @@ class binary_reader return false; } auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); } return get_ubjson_size_value(result.first); @@ -2070,7 +2084,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); } string_t s(1, static_cast(current)); return sax->string(s); @@ -2091,7 +2106,8 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr)); } } } @@ -2269,7 +2285,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, + exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } switch (result_number) @@ -2295,7 +2312,8 @@ class binary_reader case token_type::end_of_input: case token_type::literal_or_value: default: - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, + exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } } @@ -2451,7 +2469,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); } return true; } @@ -2501,7 +2519,7 @@ class binary_reader JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } - return error_msg + " " + context + ": " + detail; + return concat(error_msg, ' ', context, ": ", detail); } private: diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index fd9dab87a..406008167 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace nlohmann { @@ -224,7 +225,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -250,7 +251,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -405,7 +406,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -475,7 +476,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); } return true; diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 024dd040f..32b360969 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace nlohmann @@ -95,7 +96,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"), BasicJsonType())); + exception_message(token_type::end_of_input, "value"), nullptr)); } // in case of an error, return discarded value @@ -122,7 +123,7 @@ class parser { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); } // in case of an error, return discarded value @@ -160,7 +161,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); } return result; @@ -206,7 +207,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { @@ -218,7 +219,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); } // remember we are now inside an object @@ -261,7 +262,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)); } if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) @@ -331,7 +332,7 @@ class parser // using "uninitialized" to avoid "expected" message return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); } case token_type::uninitialized: @@ -345,7 +346,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); } } } @@ -391,7 +392,7 @@ class parser return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr)); } // states.back() is false -> object @@ -404,7 +405,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) @@ -417,7 +418,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); } // parse values @@ -445,7 +446,7 @@ class parser return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr)); } } @@ -461,24 +462,24 @@ class parser if (!context.empty()) { - error_msg += "while parsing " + context + " "; + error_msg += concat("while parsing ", context, ' '); } error_msg += "- "; if (last_token == token_type::parse_error) { - error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + - m_lexer.get_token_string() + "'"; + error_msg += concat(m_lexer.get_error_message(), "; last read: '", + m_lexer.get_token_string(), '\''); } else { - error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + error_msg += concat("unexpected ", lexer_t::token_type_name(last_token)); } if (expected != token_type::uninitialized) { - error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + error_msg += concat("; expected ", lexer_t::token_type_name(expected)); } return error_msg; diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index d8060786e..cdcdaff1f 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -285,7 +285,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci } case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); case value_t::string: case value_t::boolean: @@ -301,7 +301,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); } } } @@ -343,7 +343,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); } } } @@ -460,7 +460,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } JSON_ASSERT(m_object != nullptr); @@ -505,7 +505,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } JSON_ASSERT(m_object != nullptr); @@ -513,7 +513,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object)); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -569,7 +569,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); case value_t::array: { @@ -648,7 +648,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -677,13 +677,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object)); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); case value_t::string: case value_t::boolean: @@ -699,7 +699,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); } } } @@ -717,7 +717,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object)); } /*! diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index caed67c9e..f94f3bdf9 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -2,6 +2,8 @@ #include // all_of #include // isdigit +#include // errno, ERANGE +#include // strtoull #include // max #include // accumulate #include // string @@ -10,6 +12,7 @@ #include #include +#include #include #include @@ -18,35 +21,53 @@ namespace nlohmann /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document /// @sa https://json.nlohmann.me/api/json_pointer/ -template +template class json_pointer { // allow basic_json to access private members NLOHMANN_BASIC_JSON_TPL_DECLARATION friend class basic_json; + template + friend class json_pointer; + + template + struct string_t_helper + { + using type = T; + }; + + NLOHMANN_BASIC_JSON_TPL_DECLARATION + struct string_t_helper + { + using type = StringType; + }; + public: + // for backwards compatibility accept BasicJsonType + using string_t = typename string_t_helper::type; + /// @brief create JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ - explicit json_pointer(const std::string& s = "") + explicit json_pointer(const string_t& s = "") : reference_tokens(split(s)) {} /// @brief return a string representation of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ - std::string to_string() const + string_t to_string() const { return std::accumulate(reference_tokens.begin(), reference_tokens.end(), - std::string{}, - [](const std::string & a, const std::string & b) + string_t{}, + [](const string_t& a, const string_t& b) { - return a + "/" + detail::escape(b); + return detail::concat(a, '/', detail::escape(b)); }); } /// @brief return a string representation of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ - operator std::string() const + operator string_t() const { return to_string(); } @@ -63,7 +84,7 @@ class json_pointer /// @brief append an unescaped reference token at the end of this JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ - json_pointer& operator/=(std::string token) + json_pointer& operator/=(string_t token) { push_back(std::move(token)); return *this; @@ -86,7 +107,7 @@ class json_pointer /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ - friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param) + friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param) { return json_pointer(lhs) /= std::move(token); } @@ -118,7 +139,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); } reference_tokens.pop_back(); @@ -126,11 +147,11 @@ class json_pointer /// @brief return last reference token /// @sa https://json.nlohmann.me/api/json_pointer/back/ - const std::string& back() const + const string_t& back() const { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); } return reference_tokens.back(); @@ -138,14 +159,14 @@ class json_pointer /// @brief append an unescaped token at the end of the reference pointer /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ - void push_back(const std::string& token) + void push_back(const string_t& token) { reference_tokens.push_back(token); } /// @brief append an unescaped token at the end of the reference pointer /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ - void push_back(std::string&& token) + void push_back(string_t&& token) { reference_tokens.push_back(std::move(token)); } @@ -168,44 +189,39 @@ class json_pointer @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 typename BasicJsonType::size_type array_index(const std::string& s) + template + static typename BasicJsonType::size_type array_index(const string_t& s) { using size_type = typename BasicJsonType::size_type; // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { - JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); } - std::size_t processed_chars = 0; - unsigned long long res = 0; // NOLINT(runtime/int) - JSON_TRY + const char* p = s.c_str(); + char* p_end = nullptr; + errno = 0; // strtoull doesn't reset errno + unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) + if (p == p_end // invalid input or empty string + || errno == ERANGE // out of range + || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read { - res = std::stoull(s, &processed_chars); - } - JSON_CATCH(std::out_of_range&) - { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); - } - - // check if the string was completely read - if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) - { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); } // only triggered on special platforms (like 32bit), see also // https://github.com/nlohmann/json/pull/2203 if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) { - JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE } return static_cast(res); @@ -216,7 +232,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); } json_pointer result = *this; @@ -233,6 +249,7 @@ class json_pointer @throw parse_error.109 if array index is not a number @throw type_error.313 if value cannot be unflattened */ + template BasicJsonType& get_and_create(BasicJsonType& j) const { auto* result = &j; @@ -268,7 +285,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](array_index(reference_token)); + result = &result->operator[](array_index(reference_token)); break; } @@ -286,7 +303,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); } } @@ -312,6 +329,7 @@ class json_pointer @throw parse_error.109 if an array index was not a number @throw out_of_range.404 if the JSON pointer can not be resolved */ + template BasicJsonType& get_unchecked(BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -352,7 +370,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[](array_index(reference_token)); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -366,7 +384,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -379,6 +397,7 @@ class json_pointer @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ + template BasicJsonType& get_checked(BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -397,13 +416,13 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range", *ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat( + "array index '-' (", std::to_string(ptr->m_value.array->size()), + ") is out of range"), ptr)); } // note: at performs range check - ptr = &ptr->at(array_index(reference_token)); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -416,7 +435,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -436,6 +455,7 @@ class json_pointer @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ + template const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -454,11 +474,11 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); } // use unchecked array access - ptr = &ptr->operator[](array_index(reference_token)); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -471,7 +491,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -484,6 +504,7 @@ class json_pointer @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ + template const BasicJsonType& get_checked(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -502,13 +523,13 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range", *ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat( + "array index '-' (", std::to_string(ptr->m_value.array->size()), + ") is out of range"), ptr)); } // note: at performs range check - ptr = &ptr->at(array_index(reference_token)); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -521,7 +542,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -532,6 +553,7 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number */ + template bool contains(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -579,7 +601,7 @@ class json_pointer } } - const auto idx = array_index(reference_token); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range @@ -620,9 +642,9 @@ class json_pointer @throw parse_error.107 if the pointer is not empty or begins with '/' @throw parse_error.108 if character '~' is not followed by '0' or '1' */ - static std::vector split(const std::string& reference_string) + static std::vector split(const string_t& reference_string) { - std::vector result; + std::vector result; // special case: empty reference string -> no reference tokens if (reference_string.empty()) @@ -633,7 +655,7 @@ class json_pointer // check if nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { - JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); } // extract the reference tokens: @@ -644,11 +666,11 @@ class json_pointer std::size_t slash = reference_string.find_first_of('/', 1), // set the beginning of the first reference token start = 1; - // we can stop if start == 0 (if slash == std::string::npos) + // we can stop if start == 0 (if slash == string_t::npos) start != 0; // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = (slash == std::string::npos) ? 0 : slash + 1, + // (will eventually be 0 if slash == string_t::npos) + start = (slash == string_t::npos) ? 0 : slash + 1, // find next slash slash = reference_string.find_first_of('/', start)) { @@ -658,7 +680,7 @@ class json_pointer // check reference tokens are properly escaped for (std::size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; + pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1)) { JSON_ASSERT(reference_token[pos] == '~'); @@ -668,7 +690,7 @@ class json_pointer (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } } @@ -688,7 +710,8 @@ class json_pointer @note Empty objects or arrays are flattened to `null`. */ - static void flatten(const std::string& reference_string, + template + static void flatten(const string_t& reference_string, const BasicJsonType& value, BasicJsonType& result) { @@ -706,7 +729,7 @@ class json_pointer // iterate array and use index as reference string for (std::size_t i = 0; i < value.m_value.array->size(); ++i) { - flatten(reference_string + "/" + std::to_string(i), + flatten(detail::concat(reference_string, '/', std::to_string(i)), value.m_value.array->operator[](i), result); } } @@ -725,7 +748,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { - flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); } } break; @@ -758,12 +781,13 @@ class json_pointer @throw type_error.315 if object values are not primitive @throw type_error.313 if value cannot be unflattened */ + template static BasicJsonType unflatten(const BasicJsonType& value) { if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); } BasicJsonType result; @@ -773,7 +797,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); } // assign value to reference pointed to by JSON pointer; Note that if @@ -786,6 +810,21 @@ class json_pointer return result; } + // can't use conversion operator because of ambiguity + json_pointer convert() const& + { + json_pointer result; + result.reference_tokens = reference_tokens; + return result; + } + + json_pointer convert()&& + { + json_pointer result; + result.reference_tokens = std::move(reference_tokens); + return result; + } + /*! @brief compares two JSON pointers for equality @@ -797,11 +836,10 @@ class json_pointer @exceptionsafety No-throw guarantee: this function never throws exceptions. */ - friend bool operator==(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return lhs.reference_tokens == rhs.reference_tokens; - } + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept; /*! @brief compares two JSON pointers for inequality @@ -814,13 +852,27 @@ class json_pointer @exceptionsafety No-throw guarantee: this function never throws exceptions. */ - friend bool operator!=(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return !(lhs == rhs); - } + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept; /// the reference tokens - std::vector reference_tokens; + std::vector reference_tokens; }; + +// functions cannot be defined inside class due to ODR violations +template +inline bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept +{ + return lhs.reference_tokens == rhs.reference_tokens; +} + +template +inline bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept +{ + return !(lhs == rhs); +} } // namespace nlohmann diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 0a320e6e2..376c00a09 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -44,6 +44,21 @@ template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +template struct is_json_pointer : std::false_type {}; + +template +struct is_json_pointer> : std::true_type {}; + ////////////////////// // json_ref helpers // ////////////////////// diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 598587141..c018b8ccd 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace nlohmann { @@ -67,7 +68,7 @@ class binary_writer case value_t::discarded: default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j)); } } } @@ -937,7 +938,7 @@ class binary_writer const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); static_cast(j); } @@ -1062,7 +1063,7 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j)); } } diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index e475ef959..b6349ea8f 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace nlohmann @@ -500,7 +501,7 @@ class serializer { case error_handler_t::strict: { - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + hex_bytes(byte | 0), BasicJsonType())); + JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr)); } case error_handler_t::ignore: @@ -592,7 +593,7 @@ class serializer { case error_handler_t::strict: { - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + hex_bytes(static_cast(s.back() | 0)), BasicJsonType())); + JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast(s.back() | 0))), nullptr)); } case error_handler_t::ignore: diff --git a/include/nlohmann/detail/string_concat.hpp b/include/nlohmann/detail/string_concat.hpp new file mode 100644 index 000000000..12f62289f --- /dev/null +++ b/include/nlohmann/detail/string_concat.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include // strlen +#include // string +#include // forward + +#include +#include + +namespace nlohmann +{ +namespace detail +{ + +inline std::size_t concat_length() +{ + return 0; +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest); + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest); + +template +inline std::size_t concat_length(const char /*c*/, Args&& ... rest) +{ + return 1 + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest) +{ + // cppcheck-suppress ignoredReturnValue + return ::strlen(cstr) + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest) +{ + return str.size() + concat_length(std::forward(rest)...); +} + +template +inline void concat_into(OutStringType& /*out*/) +{} + +template +using string_can_append = decltype(std::declval().append(std::declval < Arg && > ())); + +template +using detect_string_can_append = is_detected; + +template +using string_can_append_op = decltype(std::declval() += std::declval < Arg && > ()); + +template +using detect_string_can_append_op = is_detected; + +template +using string_can_append_iter = decltype(std::declval().append(std::declval().begin(), std::declval().end())); + +template +using detect_string_can_append_iter = is_detected; + +template +using string_can_append_data = decltype(std::declval().append(std::declval().data(), std::declval().size())); + +template +using detect_string_can_append_data = is_detected; + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > = 0 > +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template::value, int> = 0> +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest) +{ + out.append(std::forward(arg)); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > > +inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest) +{ + out += std::forward(arg); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.begin(), arg.end()); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.data(), arg.size()); + concat_into(out, std::forward(rest)...); +} + +template +inline OutStringType concat(Args && ... args) +{ + OutStringType str; + str.reserve(concat_length(std::forward(args)...)); + concat_into(str, std::forward(args)...); + return str; +} + +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 84f7da52e..8911239df 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include namespace nlohmann @@ -21,12 +20,13 @@ enforced with an assertion.** @since version 2.0.0 */ -inline void replace_substring(std::string& s, const std::string& f, - const std::string& t) +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) { JSON_ASSERT(!f.empty()); for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found + pos != StringType::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and pos = s.find(f, pos + t.size())) // find next occurrence of f {} @@ -39,10 +39,11 @@ inline void replace_substring(std::string& s, const std::string& f, * * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ -inline std::string escape(std::string s) +template +inline StringType escape(StringType s) { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); return s; } @@ -53,10 +54,11 @@ inline std::string escape(std::string s) * * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -static void unescape(std::string& s) +template +static void unescape(StringType& s) { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); } } // namespace detail diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index b77670803..1a2da8d45 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -83,6 +83,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -129,7 +130,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { private: template friend struct detail::external_constructor; - friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::json_pointer; + // can be restored when json_pointer backwards compatibility is removed + // friend ::nlohmann::json_pointer; template friend class ::nlohmann::detail::parser; @@ -188,7 +193,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec public: using value_t = detail::value_t; /// JSON Pointer, see @ref nlohmann::json_pointer - using json_pointer = ::nlohmann::json_pointer; + using json_pointer = ::nlohmann::json_pointer; template using json_serializer = JSONSerializer; /// how to treat decoding errors @@ -280,9 +285,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = - std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + - std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + - std::to_string(NLOHMANN_JSON_VERSION_PATCH); + detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_PATCH)); result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; @@ -304,7 +309,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #elif defined(__clang__) result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; + result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( + std::to_string(__GNUC__), '.', + std::to_string(__GNUC_MINOR__), '.', + std::to_string(__GNUC_PATCHLEVEL__)) + } + }; #elif defined(__HP_cc) || defined(__HP_aCC) result["compiler"] = "hp" #elif defined(__IBMCPP__) @@ -532,7 +542,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", nullptr)); // LCOV_EXCL_LINE } break; } @@ -930,7 +940,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // if object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { - JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); } } @@ -1042,7 +1052,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); } // copy type from first iterator @@ -1060,7 +1070,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object)); } break; } @@ -1129,7 +1139,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::null: case value_t::discarded: default: - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object)); } set_parents(); @@ -1413,7 +1423,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this)); } /// get a pointer to the value (object) @@ -1534,7 +1544,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj)); } public: @@ -1910,7 +1920,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); } return *get_ptr(); @@ -1922,7 +1932,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); } return *get_ptr(); @@ -1953,12 +1963,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -1976,12 +1986,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -1999,12 +2009,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -2022,12 +2032,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -2074,7 +2084,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); } /// @brief access specified array element @@ -2087,7 +2097,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); } /// @brief access specified object element @@ -2108,7 +2118,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element @@ -2122,7 +2132,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element @@ -2145,7 +2155,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element @@ -2161,7 +2171,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element with default value @@ -2185,7 +2195,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return default_value; } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } /// @brief access specified object element with default value @@ -2216,7 +2226,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + template::value, int>::type = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const + { + return value(ptr.convert(), default_value); } /// @brief access specified object element via JSON Pointer with default value @@ -2228,6 +2246,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return value(ptr, string_t(default_value)); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + JSON_HEDLEY_NON_NULL(3) + string_t value(const typename ::nlohmann::json_pointer& ptr, const char* default_value) const + { + return value(ptr.convert(), default_value); + } + /// @brief access the first element /// @sa https://json.nlohmann.me/api/basic_json/front/ reference front() @@ -2271,7 +2297,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } IteratorType result = end(); @@ -2287,7 +2313,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { - JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + JSON_THROW(invalid_iterator::create(205, "iterator out of range", this)); } if (is_string()) @@ -2325,7 +2351,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::null: case value_t::discarded: default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } return result; @@ -2342,7 +2368,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); } IteratorType result = end(); @@ -2359,7 +2385,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", this)); } if (is_string()) @@ -2399,7 +2425,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::null: case value_t::discarded: default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } return result; @@ -2415,7 +2441,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } /// @brief remove element from a JSON array given an index @@ -2427,14 +2453,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } } @@ -2490,7 +2516,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ template < typename KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value, int >::type = 0 > + !detail::is_json_pointer::type>::value, int >::type = 0 > bool contains(KeyT && key) const { return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); @@ -2503,6 +2529,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.contains(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + bool contains(const typename ::nlohmann::json_pointer ptr) const + { + return ptr.contains(this); + } + /// @} @@ -2842,7 +2875,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } // transform null object into an array @@ -2875,7 +2908,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } // transform null object into an array @@ -2907,7 +2940,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } // transform null object into an object @@ -2963,7 +2996,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); } // transform null object into an array @@ -2988,7 +3021,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); } // transform null object into an object @@ -3042,14 +3075,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // insert to array and return iterator return insert_iterator(pos, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } /// @brief inserts element into array @@ -3069,14 +3102,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // insert to array and return iterator return insert_iterator(pos, cnt, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } /// @brief inserts range of elements into array @@ -3086,24 +3119,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this)); } // insert to array and return iterator @@ -3117,13 +3150,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // insert to array and return iterator @@ -3137,19 +3170,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this)); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -3176,19 +3209,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object)); + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object)); } for (auto it = first; it != last; ++it) @@ -3249,7 +3282,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -3264,7 +3297,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -3279,7 +3312,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -3294,7 +3327,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -3309,7 +3342,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -4191,6 +4224,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference operator[](const ::nlohmann::json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + /// @brief access specified element via JSON Pointer /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ const_reference operator[](const json_pointer& ptr) const @@ -4198,6 +4238,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference operator[](const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + /// @brief access specified element via JSON Pointer /// @sa https://json.nlohmann.me/api/basic_json/at/ reference at(const json_pointer& ptr) @@ -4205,6 +4252,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_checked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference at(const ::nlohmann::json_pointer& ptr) + { + return ptr.get_checked(this); + } + /// @brief access specified element via JSON Pointer /// @sa https://json.nlohmann.me/api/basic_json/at/ const_reference at(const json_pointer& ptr) const @@ -4212,6 +4266,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_checked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference at(const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_checked(this); + } + /// @brief return flattened JSON value /// @sa https://json.nlohmann.me/api/basic_json/flatten/ basic_json flatten() const @@ -4318,11 +4379,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - const auto idx = json_pointer::array_index(last_path); + const auto idx = json_pointer::template array_index(last_path); 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", parent)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent)); } // default case: insert add offset @@ -4363,20 +4424,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this)); } } else if (parent.is_array()) { // note erase performs range check - parent.erase(json_pointer::array_index(last_path)); + parent.erase(json_pointer::template array_index(last_path)); } }; // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch)); } // iterate and apply the operations @@ -4391,20 +4452,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec auto it = val.m_value.object->find(member); // context-sensitive error message - const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val)); } // no error: return value @@ -4414,7 +4475,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val)); } // collect mandatory members @@ -4492,7 +4553,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); } break; @@ -4503,7 +4564,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val)); } } } @@ -4545,7 +4606,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec while (i < source.size() && i < target.size()) { // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i))); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } @@ -4562,7 +4623,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.insert(result.begin() + end_index, object( { {"op", "remove"}, - {"path", path + "/" + std::to_string(i)} + {"path", detail::concat(path, '/', std::to_string(i))} })); ++i; } @@ -4573,7 +4634,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.push_back( { {"op", "add"}, - {"path", path + "/-"}, + {"path", detail::concat(path, "/-")}, {"value", target[i]} }); ++i; @@ -4588,7 +4649,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto path_key = path + "/" + detail::escape(it.key()); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); if (target.find(it.key()) != target.end()) { @@ -4612,7 +4673,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto path_key = path + "/" + detail::escape(it.key()); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); result.push_back( { {"op", "add"}, {"path", path_key}, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 53c15b651..a29c52997 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -89,6 +89,7 @@ SOFTWARE. // #include +#include // nullptr_t #include // exception #include // runtime_error #include // to_string @@ -180,7 +181,6 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept // #include -#include // #include @@ -2748,12 +2748,13 @@ enforced with an assertion.** @since version 2.0.0 */ -inline void replace_substring(std::string& s, const std::string& f, - const std::string& t) +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) { JSON_ASSERT(!f.empty()); for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found + pos != StringType::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and pos = s.find(f, pos + t.size())) // find next occurrence of f {} @@ -2766,10 +2767,11 @@ inline void replace_substring(std::string& s, const std::string& f, * * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ -inline std::string escape(std::string s) +template +inline StringType escape(StringType s) { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); return s; } @@ -2780,10 +2782,11 @@ inline std::string escape(std::string s) * * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -static void unescape(std::string& s) +template +static void unescape(StringType& s) { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); } } // namespace detail @@ -2820,234 +2823,6 @@ struct position_t // #include - -namespace nlohmann -{ -namespace detail -{ -//////////////// -// exceptions // -//////////////// - -/// @brief general exception of the @ref basic_json class -/// @sa https://json.nlohmann.me/api/basic_json/exception/ -class exception : public std::exception -{ - public: - /// returns the explanatory string - const char* what() const noexcept override - { - return m.what(); - } - - /// the id of the exception - const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) - - protected: - JSON_HEDLEY_NON_NULL(3) - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) - - static std::string name(const std::string& ename, int id_) - { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; - } - - template - static std::string diagnostics(const BasicJsonType& leaf_element) - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (¤t->m_parent->m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (&element.second == current) - { - tokens.emplace_back(element.first.c_str()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + detail::escape(b); - }) + ") "; -#else - static_cast(leaf_element); - return ""; -#endif - } - - private: - /// an exception object as storage for error messages - std::runtime_error m; -}; - -/// @brief exception indicating a parse error -/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ -class parse_error : public exception -{ - public: - /*! - @brief create a parse error exception - @param[in] id_ the id of the exception - @param[in] pos the position where the error occurred (or with - chars_read_total=0 if the position cannot be - determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - template - static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + exception::diagnostics(context) + what_arg; - return {id_, pos.chars_read_total, w.c_str()}; - } - - template - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + exception::diagnostics(context) + what_arg; - return {id_, byte_, w.c_str()}; - } - - /*! - @brief byte index of the parse error - - The byte index of the last read character in the input file. - - @note For an input with n bytes, 1 is the index of the first character and - n+1 is the index of the terminating null byte or the end of file. - This also holds true when reading a byte vector (CBOR or MessagePack). - */ - const std::size_t byte; - - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} - - static std::string position_string(const position_t& pos) - { - return " at line " + std::to_string(pos.lines_read + 1) + - ", column " + std::to_string(pos.chars_read_current_line); - } -}; - -/// @brief exception indicating errors with iterators -/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ -class invalid_iterator : public exception -{ - public: - template - static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; - -/// @brief exception indicating executing a member function with a wrong type -/// @sa https://json.nlohmann.me/api/basic_json/type_error/ -class type_error : public exception -{ - public: - template - static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/// @brief exception indicating access out of the defined range -/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ -class out_of_range : public exception -{ - public: - template - static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/// @brief exception indicating other library errors -/// @sa https://json.nlohmann.me/api/basic_json/other_error/ -class other_error : public exception -{ - public: - template - static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -} // namespace detail -} // namespace nlohmann - -// #include - // #include @@ -3209,18 +2984,6 @@ struct static_const } // namespace detail } // namespace nlohmann -// #include - - -namespace nlohmann -{ -namespace detail -{ -// dispatching helper struct -template struct identity_tag {}; -} // namespace detail -} // namespace nlohmann - // #include @@ -3410,6 +3173,21 @@ template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +template struct is_json_pointer : std::false_type {}; + +template +struct is_json_pointer> : std::true_type {}; + ////////////////////// // json_ref helpers // ////////////////////// @@ -3839,6 +3617,402 @@ T conditional_static_cast(U value) } // namespace detail } // namespace nlohmann +// #include + + +#include // strlen +#include // string +#include // forward + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +inline std::size_t concat_length() +{ + return 0; +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest); + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest); + +template +inline std::size_t concat_length(const char /*c*/, Args&& ... rest) +{ + return 1 + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest) +{ + // cppcheck-suppress ignoredReturnValue + return ::strlen(cstr) + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest) +{ + return str.size() + concat_length(std::forward(rest)...); +} + +template +inline void concat_into(OutStringType& /*out*/) +{} + +template +using string_can_append = decltype(std::declval().append(std::declval < Arg && > ())); + +template +using detect_string_can_append = is_detected; + +template +using string_can_append_op = decltype(std::declval() += std::declval < Arg && > ()); + +template +using detect_string_can_append_op = is_detected; + +template +using string_can_append_iter = decltype(std::declval().append(std::declval().begin(), std::declval().end())); + +template +using detect_string_can_append_iter = is_detected; + +template +using string_can_append_data = decltype(std::declval().append(std::declval().data(), std::declval().size())); + +template +using detect_string_can_append_data = is_detected; + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > = 0 > +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template::value, int> = 0> +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest) +{ + out.append(std::forward(arg)); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > > +inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest) +{ + out += std::forward(arg); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.begin(), arg.end()); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.data(), arg.size()); + concat_into(out, std::forward(rest)...); +} + +template +inline OutStringType concat(Args && ... args) +{ + OutStringType str; + str.reserve(concat_length(std::forward(args)...)); + concat_into(str, std::forward(args)...); + return str; +} + +} // namespace detail +} // namespace nlohmann + + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/// @brief general exception of the @ref basic_json class +/// @sa https://json.nlohmann.me/api/basic_json/exception/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) + + static std::string name(const std::string& ename, int id_) + { + return concat("[json.exception.", ename, '.', std::to_string(id_), "] "); + } + + static std::string diagnostics(std::nullptr_t /*leaf_element*/) + { + return ""; + } + + template + static std::string diagnostics(const BasicJsonType* leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return concat(a, '/', detail::escape(b)); + }); + return concat('(', str, ") "); +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/// @brief exception indicating a parse error +/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template::value, int> = 0> + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context) + { + std::string w = concat(exception::name("parse_error", id_), "parse error", + position_string(pos), ": ", exception::diagnostics(context), what_arg); + return {id_, pos.chars_read_total, w.c_str()}; + } + + template::value, int> = 0> + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context) + { + std::string w = concat(exception::name("parse_error", id_), "parse error", + (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""), + ": ", exception::diagnostics(context), what_arg); + return {id_, byte_, w.c_str()}; + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return concat(" at line ", std::to_string(pos.lines_read + 1), + ", column ", std::to_string(pos.chars_read_current_line)); + } +}; + +/// @brief exception indicating errors with iterators +/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ +class invalid_iterator : public exception +{ + public: + template::value, int> = 0> + static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context) + { + std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/// @brief exception indicating executing a member function with a wrong type +/// @sa https://json.nlohmann.me/api/basic_json/type_error/ +class type_error : public exception +{ + public: + template::value, int> = 0> + static type_error create(int id_, const std::string& what_arg, BasicJsonContext context) + { + std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating access out of the defined range +/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ +class out_of_range : public exception +{ + public: + template::value, int> = 0> + static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context) + { + std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating other library errors +/// @sa https://json.nlohmann.me/api/basic_json/other_error/ +class other_error : public exception +{ + public: + template::value, int> = 0> + static other_error create(int id_, const std::string& what_arg, BasicJsonContext context) + { + std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + // #include @@ -3865,7 +4039,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { if (JSON_HEDLEY_UNLIKELY(!j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j)); } n = nullptr; } @@ -3903,7 +4077,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); } } @@ -3912,7 +4086,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j)); } b = *j.template get_ptr(); } @@ -3922,7 +4096,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } s = *j.template get_ptr(); } @@ -3937,7 +4111,7 @@ void from_json(const BasicJsonType& j, StringType& s) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } s = *j.template get_ptr(); @@ -3977,7 +4151,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } l.clear(); std::transform(j.rbegin(), j.rend(), @@ -3994,7 +4168,7 @@ void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } l.resize(j.size()); std::transform(j.begin(), j.end(), std::begin(l), @@ -4091,7 +4265,7 @@ void()) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } from_json_array_impl(j, arr, priority_tag<3> {}); @@ -4110,7 +4284,7 @@ auto from_json(BasicJsonType&& j, identity_tag> tag) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); @@ -4121,7 +4295,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j)); } bin = *j.template get_ptr(); @@ -4133,7 +4307,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_HEDLEY_UNLIKELY(!j.is_object())) { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); } ConstructibleObjectType ret; @@ -4193,7 +4367,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); } } @@ -4234,7 +4408,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); @@ -4247,14 +4421,14 @@ void from_json(const BasicJsonType& j, std::map& { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); } m.clear(); for (const auto& p : j) { if (JSON_HEDLEY_UNLIKELY(!p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j)); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -4267,14 +4441,14 @@ void from_json(const BasicJsonType& j, std::unordered_map(), p.at(1).template get()); } @@ -4286,7 +4460,7 @@ void from_json(const BasicJsonType& j, std_fs::path& p) { if (JSON_HEDLEY_UNLIKELY(!j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); } p = *j.template get_ptr(); } @@ -5730,6 +5904,8 @@ class span_input_adapter // #include +// #include + namespace nlohmann { @@ -5947,7 +6123,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -5973,7 +6149,7 @@ class json_sax_dom_parser if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -6128,7 +6304,7 @@ class json_sax_dom_callback_parser // check object limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -6198,7 +6374,7 @@ class json_sax_dom_callback_parser // check array limit if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); } return true; @@ -8218,6 +8394,8 @@ struct is_sax_static_asserts // #include +// #include + // #include @@ -8338,8 +8516,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { - return sax->parse_error(chars_read, get_token_string(), - parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, + exception_message(format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); } } @@ -8415,7 +8593,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr)); } return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); @@ -8436,7 +8615,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(len < 0)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr)); } // All BSON binary values have a subtype @@ -8518,7 +8698,9 @@ class binary_reader { std::array cr{{}}; static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + std::string cr_str{cr.data()}; + return sax->parse_error(element_type_parse_position, cr_str, + parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr)); } } } @@ -8918,7 +9100,8 @@ class binary_reader case cbor_tag_handler_t::error: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); } case cbor_tag_handler_t::ignore: @@ -9075,7 +9258,8 @@ class binary_reader default: // anything else (0xFF is handled inside the other types) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); } } } @@ -9170,7 +9354,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr)); } } } @@ -9269,7 +9454,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr)); } } } @@ -9739,7 +9925,8 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr)); } } } @@ -9821,7 +10008,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr)); } } } @@ -10071,7 +10259,8 @@ class binary_reader default: auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr)); } } @@ -10141,7 +10330,8 @@ class binary_reader default: { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr)); } } } @@ -10179,7 +10369,8 @@ class binary_reader return false; } auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); } return get_ubjson_size_value(result.first); @@ -10269,7 +10460,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, + exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); } string_t s(1, static_cast(current)); return sax->string(s); @@ -10290,7 +10482,8 @@ class binary_reader default: // anything else { auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, + exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr)); } } } @@ -10468,7 +10661,8 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, + exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } switch (result_number) @@ -10494,7 +10688,8 @@ class binary_reader case token_type::end_of_input: case token_type::literal_or_value: default: - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, + exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } } @@ -10650,7 +10845,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error(chars_read, "", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); } return true; } @@ -10700,7 +10895,7 @@ class binary_reader JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE } - return error_msg + " " + context + ": " + detail; + return concat(error_msg, ' ', context, ": ", detail); } private: @@ -10748,6 +10943,8 @@ class binary_reader // #include +// #include + // #include @@ -10831,7 +11028,7 @@ class parser sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"), BasicJsonType())); + exception_message(token_type::end_of_input, "value"), nullptr)); } // in case of an error, return discarded value @@ -10858,7 +11055,7 @@ class parser { sdp.parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); } // in case of an error, return discarded value @@ -10896,7 +11093,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); } return result; @@ -10942,7 +11139,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) { @@ -10954,7 +11151,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); } // remember we are now inside an object @@ -10997,7 +11194,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)); } if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) @@ -11067,7 +11264,7 @@ class parser // using "uninitialized" to avoid "expected" message return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); } case token_type::uninitialized: @@ -11081,7 +11278,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); } } } @@ -11127,7 +11324,7 @@ class parser return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr)); } // states.back() is false -> object @@ -11140,7 +11337,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); } if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) @@ -11153,7 +11350,7 @@ class parser { return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); } // parse values @@ -11181,7 +11378,7 @@ class parser return sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr)); } } @@ -11197,24 +11394,24 @@ class parser if (!context.empty()) { - error_msg += "while parsing " + context + " "; + error_msg += concat("while parsing ", context, ' '); } error_msg += "- "; if (last_token == token_type::parse_error) { - error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + - m_lexer.get_token_string() + "'"; + error_msg += concat(m_lexer.get_error_message(), "; last read: '", + m_lexer.get_token_string(), '\''); } else { - error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + error_msg += concat("unexpected ", lexer_t::token_type_name(last_token)); } if (expected != token_type::uninitialized) { - error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + error_msg += concat("; expected ", lexer_t::token_type_name(expected)); } return error_msg; @@ -11681,7 +11878,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci } case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); case value_t::string: case value_t::boolean: @@ -11697,7 +11894,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); } } } @@ -11739,7 +11936,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); } } } @@ -11856,7 +12053,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } JSON_ASSERT(m_object != nullptr); @@ -11901,7 +12098,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); } JSON_ASSERT(m_object != nullptr); @@ -11909,7 +12106,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object)); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -11965,7 +12162,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); case value_t::array: { @@ -12044,7 +12241,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -12073,13 +12270,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object)); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); case value_t::string: case value_t::boolean: @@ -12095,7 +12292,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); } } } @@ -12113,7 +12310,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object)); } /*! @@ -12264,6 +12461,8 @@ class json_reverse_iterator : public std::reverse_iterator #include // all_of #include // isdigit +#include // errno, ERANGE +#include // strtoull #include // max #include // accumulate #include // string @@ -12274,6 +12473,8 @@ class json_reverse_iterator : public std::reverse_iterator // #include +// #include + // #include // #include @@ -12284,35 +12485,53 @@ namespace nlohmann /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document /// @sa https://json.nlohmann.me/api/json_pointer/ -template +template class json_pointer { // allow basic_json to access private members NLOHMANN_BASIC_JSON_TPL_DECLARATION friend class basic_json; + template + friend class json_pointer; + + template + struct string_t_helper + { + using type = T; + }; + + NLOHMANN_BASIC_JSON_TPL_DECLARATION + struct string_t_helper + { + using type = StringType; + }; + public: + // for backwards compatibility accept BasicJsonType + using string_t = typename string_t_helper::type; + /// @brief create JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ - explicit json_pointer(const std::string& s = "") + explicit json_pointer(const string_t& s = "") : reference_tokens(split(s)) {} /// @brief return a string representation of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ - std::string to_string() const + string_t to_string() const { return std::accumulate(reference_tokens.begin(), reference_tokens.end(), - std::string{}, - [](const std::string & a, const std::string & b) + string_t{}, + [](const string_t& a, const string_t& b) { - return a + "/" + detail::escape(b); + return detail::concat(a, '/', detail::escape(b)); }); } /// @brief return a string representation of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ - operator std::string() const + operator string_t() const { return to_string(); } @@ -12329,7 +12548,7 @@ class json_pointer /// @brief append an unescaped reference token at the end of this JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ - json_pointer& operator/=(std::string token) + json_pointer& operator/=(string_t token) { push_back(std::move(token)); return *this; @@ -12352,7 +12571,7 @@ class json_pointer /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ - friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param) + friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param) { return json_pointer(lhs) /= std::move(token); } @@ -12384,7 +12603,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); } reference_tokens.pop_back(); @@ -12392,11 +12611,11 @@ class json_pointer /// @brief return last reference token /// @sa https://json.nlohmann.me/api/json_pointer/back/ - const std::string& back() const + const string_t& back() const { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); } return reference_tokens.back(); @@ -12404,14 +12623,14 @@ class json_pointer /// @brief append an unescaped token at the end of the reference pointer /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ - void push_back(const std::string& token) + void push_back(const string_t& token) { reference_tokens.push_back(token); } /// @brief append an unescaped token at the end of the reference pointer /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ - void push_back(std::string&& token) + void push_back(string_t&& token) { reference_tokens.push_back(std::move(token)); } @@ -12434,44 +12653,39 @@ class json_pointer @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 typename BasicJsonType::size_type array_index(const std::string& s) + template + static typename BasicJsonType::size_type array_index(const string_t& s) { using size_type = typename BasicJsonType::size_type; // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) { - JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) { - JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); } - std::size_t processed_chars = 0; - unsigned long long res = 0; // NOLINT(runtime/int) - JSON_TRY + const char* p = s.c_str(); + char* p_end = nullptr; + errno = 0; // strtoull doesn't reset errno + unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) + if (p == p_end // invalid input or empty string + || errno == ERANGE // out of range + || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read { - res = std::stoull(s, &processed_chars); - } - JSON_CATCH(std::out_of_range&) - { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); - } - - // check if the string was completely read - if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) - { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); } // only triggered on special platforms (like 32bit), see also // https://github.com/nlohmann/json/pull/2203 if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) { - JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE } return static_cast(res); @@ -12482,7 +12696,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(empty())) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); } json_pointer result = *this; @@ -12499,6 +12713,7 @@ class json_pointer @throw parse_error.109 if array index is not a number @throw type_error.313 if value cannot be unflattened */ + template BasicJsonType& get_and_create(BasicJsonType& j) const { auto* result = &j; @@ -12534,7 +12749,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](array_index(reference_token)); + result = &result->operator[](array_index(reference_token)); break; } @@ -12552,7 +12767,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); } } @@ -12578,6 +12793,7 @@ class json_pointer @throw parse_error.109 if an array index was not a number @throw out_of_range.404 if the JSON pointer can not be resolved */ + template BasicJsonType& get_unchecked(BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -12618,7 +12834,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[](array_index(reference_token)); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -12632,7 +12848,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -12645,6 +12861,7 @@ class json_pointer @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ + template BasicJsonType& get_checked(BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -12663,13 +12880,13 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range", *ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat( + "array index '-' (", std::to_string(ptr->m_value.array->size()), + ") is out of range"), ptr)); } // note: at performs range check - ptr = &ptr->at(array_index(reference_token)); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -12682,7 +12899,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -12702,6 +12919,7 @@ class json_pointer @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ + template const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -12720,11 +12938,11 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); } // use unchecked array access - ptr = &ptr->operator[](array_index(reference_token)); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -12737,7 +12955,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -12750,6 +12968,7 @@ class json_pointer @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ + template const BasicJsonType& get_checked(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -12768,13 +12987,13 @@ class json_pointer if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) { // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) + - ") is out of range", *ptr)); + JSON_THROW(detail::out_of_range::create(402, detail::concat( + "array index '-' (", std::to_string(ptr->m_value.array->size()), + ") is out of range"), ptr)); } // note: at performs range check - ptr = &ptr->at(array_index(reference_token)); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -12787,7 +13006,7 @@ class json_pointer case detail::value_t::binary: case detail::value_t::discarded: default: - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); } } @@ -12798,6 +13017,7 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number */ + template bool contains(const BasicJsonType* ptr) const { for (const auto& reference_token : reference_tokens) @@ -12845,7 +13065,7 @@ class json_pointer } } - const auto idx = array_index(reference_token); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range @@ -12886,9 +13106,9 @@ class json_pointer @throw parse_error.107 if the pointer is not empty or begins with '/' @throw parse_error.108 if character '~' is not followed by '0' or '1' */ - static std::vector split(const std::string& reference_string) + static std::vector split(const string_t& reference_string) { - std::vector result; + std::vector result; // special case: empty reference string -> no reference tokens if (reference_string.empty()) @@ -12899,7 +13119,7 @@ class json_pointer // check if nonempty reference string begins with slash if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) { - JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); } // extract the reference tokens: @@ -12910,11 +13130,11 @@ class json_pointer std::size_t slash = reference_string.find_first_of('/', 1), // set the beginning of the first reference token start = 1; - // we can stop if start == 0 (if slash == std::string::npos) + // we can stop if start == 0 (if slash == string_t::npos) start != 0; // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = (slash == std::string::npos) ? 0 : slash + 1, + // (will eventually be 0 if slash == string_t::npos) + start = (slash == string_t::npos) ? 0 : slash + 1, // find next slash slash = reference_string.find_first_of('/', start)) { @@ -12924,7 +13144,7 @@ class json_pointer // check reference tokens are properly escaped for (std::size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; + pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1)) { JSON_ASSERT(reference_token[pos] == '~'); @@ -12934,7 +13154,7 @@ class json_pointer (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); } } @@ -12954,7 +13174,8 @@ class json_pointer @note Empty objects or arrays are flattened to `null`. */ - static void flatten(const std::string& reference_string, + template + static void flatten(const string_t& reference_string, const BasicJsonType& value, BasicJsonType& result) { @@ -12972,7 +13193,7 @@ class json_pointer // iterate array and use index as reference string for (std::size_t i = 0; i < value.m_value.array->size(); ++i) { - flatten(reference_string + "/" + std::to_string(i), + flatten(detail::concat(reference_string, '/', std::to_string(i)), value.m_value.array->operator[](i), result); } } @@ -12991,7 +13212,7 @@ class json_pointer // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { - flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); } } break; @@ -13024,12 +13245,13 @@ class json_pointer @throw type_error.315 if object values are not primitive @throw type_error.313 if value cannot be unflattened */ + template static BasicJsonType unflatten(const BasicJsonType& value) { if (JSON_HEDLEY_UNLIKELY(!value.is_object())) { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); } BasicJsonType result; @@ -13039,7 +13261,7 @@ class json_pointer { if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); } // assign value to reference pointed to by JSON pointer; Note that if @@ -13052,6 +13274,21 @@ class json_pointer return result; } + // can't use conversion operator because of ambiguity + json_pointer convert() const& + { + json_pointer result; + result.reference_tokens = reference_tokens; + return result; + } + + json_pointer convert()&& + { + json_pointer result; + result.reference_tokens = std::move(reference_tokens); + return result; + } + /*! @brief compares two JSON pointers for equality @@ -13063,11 +13300,10 @@ class json_pointer @exceptionsafety No-throw guarantee: this function never throws exceptions. */ - friend bool operator==(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return lhs.reference_tokens == rhs.reference_tokens; - } + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept; /*! @brief compares two JSON pointers for inequality @@ -13080,15 +13316,29 @@ class json_pointer @exceptionsafety No-throw guarantee: this function never throws exceptions. */ - friend bool operator!=(json_pointer const& lhs, - json_pointer const& rhs) noexcept - { - return !(lhs == rhs); - } + template + // NOLINTNEXTLINE(readability-redundant-declaration) + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept; /// the reference tokens - std::vector reference_tokens; + std::vector reference_tokens; }; + +// functions cannot be defined inside class due to ODR violations +template +inline bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept +{ + return lhs.reference_tokens == rhs.reference_tokens; +} + +template +inline bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept +{ + return !(lhs == rhs); +} } // namespace nlohmann // #include @@ -13164,6 +13414,8 @@ class json_ref // #include +// #include + // #include // #include @@ -13327,6 +13579,8 @@ class output_adapter } // namespace detail } // namespace nlohmann +// #include + namespace nlohmann { @@ -13382,7 +13636,7 @@ class binary_writer case value_t::discarded: default: { - JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j)); } } } @@ -14252,7 +14506,7 @@ class binary_writer const auto it = name.find(static_cast(0)); if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j)); static_cast(j); } @@ -14377,7 +14631,7 @@ class binary_writer } else { - JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j)); } } @@ -16090,6 +16344,8 @@ char* to_chars(char* first, const char* last, FloatType value) // #include +// #include + // #include @@ -16572,7 +16828,7 @@ class serializer { case error_handler_t::strict: { - JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + hex_bytes(byte | 0), BasicJsonType())); + JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr)); } case error_handler_t::ignore: @@ -16664,7 +16920,7 @@ class serializer { case error_handler_t::strict: { - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + hex_bytes(static_cast(s.back() | 0)), BasicJsonType())); + JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast(s.back() | 0))), nullptr)); } case error_handler_t::ignore: @@ -17322,7 +17578,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { private: template friend struct detail::external_constructor; - friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::json_pointer; + // can be restored when json_pointer backwards compatibility is removed + // friend ::nlohmann::json_pointer; template friend class ::nlohmann::detail::parser; @@ -17381,7 +17641,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec public: using value_t = detail::value_t; /// JSON Pointer, see @ref nlohmann::json_pointer - using json_pointer = ::nlohmann::json_pointer; + using json_pointer = ::nlohmann::json_pointer; template using json_serializer = JSONSerializer; /// how to treat decoding errors @@ -17473,9 +17733,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = - std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + - std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + - std::to_string(NLOHMANN_JSON_VERSION_PATCH); + detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_PATCH)); result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; @@ -17497,7 +17757,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #elif defined(__clang__) result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; + result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( + std::to_string(__GNUC__), '.', + std::to_string(__GNUC_MINOR__), '.', + std::to_string(__GNUC_PATCHLEVEL__)) + } + }; #elif defined(__HP_cc) || defined(__HP_aCC) result["compiler"] = "hp" #elif defined(__IBMCPP__) @@ -17725,7 +17990,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec object = nullptr; // silence warning, see #821 if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", nullptr)); // LCOV_EXCL_LINE } break; } @@ -18123,7 +18388,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // if object is wanted but impossible, throw an exception if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) { - JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); } } @@ -18235,7 +18500,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); } // copy type from first iterator @@ -18253,7 +18518,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object)); } break; } @@ -18322,7 +18587,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::null: case value_t::discarded: default: - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object)); } set_parents(); @@ -18606,7 +18871,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this)); } /// get a pointer to the value (object) @@ -18727,7 +18992,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return *ptr; } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj)); } public: @@ -19103,7 +19368,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); } return *get_ptr(); @@ -19115,7 +19380,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (!is_binary()) { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); } return *get_ptr(); @@ -19146,12 +19411,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -19169,12 +19434,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -19192,12 +19457,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -19215,12 +19480,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); } } else { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); } } @@ -19267,7 +19532,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); } /// @brief access specified array element @@ -19280,7 +19545,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); } /// @brief access specified object element @@ -19301,7 +19566,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element @@ -19315,7 +19580,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element @@ -19338,7 +19603,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return set_parent(m_value.object->operator[](key)); } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element @@ -19354,7 +19619,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } /// @brief access specified object element with default value @@ -19378,7 +19643,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return default_value; } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } /// @brief access specified object element with default value @@ -19409,7 +19674,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } } - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + template::value, int>::type = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const + { + return value(ptr.convert(), default_value); } /// @brief access specified object element via JSON Pointer with default value @@ -19421,6 +19694,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return value(ptr, string_t(default_value)); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + JSON_HEDLEY_NON_NULL(3) + string_t value(const typename ::nlohmann::json_pointer& ptr, const char* default_value) const + { + return value(ptr.convert(), default_value); + } + /// @brief access the first element /// @sa https://json.nlohmann.me/api/basic_json/front/ reference front() @@ -19464,7 +19745,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } IteratorType result = end(); @@ -19480,7 +19761,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) { - JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + JSON_THROW(invalid_iterator::create(205, "iterator out of range", this)); } if (is_string()) @@ -19518,7 +19799,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::null: case value_t::discarded: default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } return result; @@ -19535,7 +19816,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // make sure iterator fits the current value if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); } IteratorType result = end(); @@ -19552,7 +19833,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() || !last.m_it.primitive_iterator.is_end())) { - JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + JSON_THROW(invalid_iterator::create(204, "iterators out of range", this)); } if (is_string()) @@ -19592,7 +19873,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::null: case value_t::discarded: default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } return result; @@ -19608,7 +19889,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return m_value.object->erase(key); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } /// @brief remove element from a JSON array given an index @@ -19620,14 +19901,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { if (JSON_HEDLEY_UNLIKELY(idx >= size())) { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); } } @@ -19683,7 +19964,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ template < typename KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value, int >::type = 0 > + !detail::is_json_pointer::type>::value, int >::type = 0 > bool contains(KeyT && key) const { return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); @@ -19696,6 +19977,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.contains(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + bool contains(const typename ::nlohmann::json_pointer ptr) const + { + return ptr.contains(this); + } + /// @} @@ -20035,7 +20323,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } // transform null object into an array @@ -20068,7 +20356,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // push_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } // transform null object into an array @@ -20100,7 +20388,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // push_back only works for null objects or objects if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); } // transform null object into an object @@ -20156,7 +20444,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // emplace_back only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); } // transform null object into an array @@ -20181,7 +20469,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // emplace only works for null objects or arrays if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); } // transform null object into an object @@ -20235,14 +20523,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // insert to array and return iterator return insert_iterator(pos, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } /// @brief inserts element into array @@ -20262,14 +20550,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // insert to array and return iterator return insert_iterator(pos, cnt, val); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } /// @brief inserts range of elements into array @@ -20279,24 +20567,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); } if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this)); } // insert to array and return iterator @@ -20310,13 +20598,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // insert only works for arrays if (JSON_HEDLEY_UNLIKELY(!is_array())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } // check if iterator pos fits to this JSON value if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); } // insert to array and return iterator @@ -20330,19 +20618,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // insert only works for objects if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this)); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); @@ -20369,19 +20657,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this)); } // check if range iterators belong to the same JSON object if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); } // passed iterators must belong to objects if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object)); + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object)); } for (auto it = first; it != last; ++it) @@ -20442,7 +20730,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -20457,7 +20745,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -20472,7 +20760,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -20487,7 +20775,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -20502,7 +20790,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + JSON_THROW(type_error::create(310, detail::concat("cannot use swap() with ", type_name()), this)); } } @@ -21384,6 +21672,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference operator[](const ::nlohmann::json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + /// @brief access specified element via JSON Pointer /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ const_reference operator[](const json_pointer& ptr) const @@ -21391,6 +21686,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_unchecked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference operator[](const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + /// @brief access specified element via JSON Pointer /// @sa https://json.nlohmann.me/api/basic_json/at/ reference at(const json_pointer& ptr) @@ -21398,6 +21700,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_checked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference at(const ::nlohmann::json_pointer& ptr) + { + return ptr.get_checked(this); + } + /// @brief access specified element via JSON Pointer /// @sa https://json.nlohmann.me/api/basic_json/at/ const_reference at(const json_pointer& ptr) const @@ -21405,6 +21714,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return ptr.get_checked(this); } + template + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference at(const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_checked(this); + } + /// @brief return flattened JSON value /// @sa https://json.nlohmann.me/api/basic_json/flatten/ basic_json flatten() const @@ -21511,11 +21827,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - const auto idx = json_pointer::array_index(last_path); + const auto idx = json_pointer::template array_index(last_path); 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", parent)); + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent)); } // default case: insert add offset @@ -21556,20 +21872,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this)); } } else if (parent.is_array()) { // note erase performs range check - parent.erase(json_pointer::array_index(last_path)); + parent.erase(json_pointer::template array_index(last_path)); } }; // type check: top level value must be an array if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch)); } // iterate and apply the operations @@ -21584,20 +21900,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec auto it = val.m_value.object->find(member); // context-sensitive error message - const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // check if desired value is present if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); } // check if result is of type string if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) { // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val)); } // no error: return value @@ -21607,7 +21923,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // type check: every element of the array must be an object if (JSON_HEDLEY_UNLIKELY(!val.is_object())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val)); } // collect mandatory members @@ -21685,7 +22001,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // throw an exception if test fails if (JSON_HEDLEY_UNLIKELY(!success)) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); } break; @@ -21696,7 +22012,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val)); } } } @@ -21738,7 +22054,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec while (i < source.size() && i < target.size()) { // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i))); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } @@ -21755,7 +22071,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.insert(result.begin() + end_index, object( { {"op", "remove"}, - {"path", path + "/" + std::to_string(i)} + {"path", detail::concat(path, '/', std::to_string(i))} })); ++i; } @@ -21766,7 +22082,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result.push_back( { {"op", "add"}, - {"path", path + "/-"}, + {"path", detail::concat(path, "/-")}, {"value", target[i]} }); ++i; @@ -21781,7 +22097,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch - const auto path_key = path + "/" + detail::escape(it.key()); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); if (target.find(it.key()) != target.end()) { @@ -21805,7 +22121,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it - const auto path_key = path + "/" + detail::escape(it.key()); + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); result.push_back( { {"op", "add"}, {"path", path_key}, diff --git a/test/src/unit-alt-string.cpp b/test/src/unit-alt-string.cpp index bcde18b75..26f66ecad 100644 --- a/test/src/unit-alt-string.cpp +++ b/test/src/unit-alt-string.cpp @@ -49,6 +49,8 @@ class alt_string public: using value_type = std::string::value_type; + static constexpr auto npos = static_cast(-1); + alt_string(const char* str): str_impl(str) {} alt_string(const char* str, std::size_t count): str_impl(str, count) {} alt_string(size_t count, char chr): str_impl(count, chr) {} @@ -144,11 +146,38 @@ class alt_string str_impl.clear(); } - const value_type* data() + const value_type* data() const { return str_impl.data(); } + bool empty() const + { + return str_impl.empty(); + } + + std::size_t find(const alt_string& str, std::size_t pos = 0) const + { + return str_impl.find(str.str_impl, pos); + } + + std::size_t find_first_of(char c, std::size_t pos = 0) const + { + return str_impl.find_first_of(c, pos); + } + + alt_string substr(std::size_t pos = 0, std::size_t count = npos) const + { + std::string s = str_impl.substr(pos, count); + return {s.data(), s.size()}; + } + + alt_string& replace(std::size_t pos, std::size_t count, const alt_string& str) + { + str_impl.replace(pos, count, str.str_impl); + return *this; + } + private: std::string str_impl {}; @@ -296,4 +325,20 @@ TEST_CASE("alternative string type") CHECK_FALSE(const_doc["Who are you?"] == "I'm Bruce Wayne"); } } + + SECTION("JSON pointer") + { + // conversion from json to alt_json fails to compile (see #3425); + // attempted fix(*) produces: [[['b','a','r'],['b','a','z']]] (with each char being an integer) + // (*) disable implicit conversion for json_refs of any basic_json type + // alt_json j = R"( + // { + // "foo": ["bar", "baz"] + // } + // )"_json; + auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})"); + + CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]); + CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]); + } } diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index f2018bf3c..666902582 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -37,6 +37,84 @@ using nlohmann::json; namespace { +struct alt_string_iter +{ + alt_string_iter() = default; + alt_string_iter(const char* cstr) + : impl(cstr) + {} + + void reserve(std::size_t s) + { + impl.reserve(s); + } + + template + void append(Iter first, Iter last) + { + impl.append(first, last); + } + + std::string::const_iterator begin() const + { + return impl.begin(); + } + + std::string::const_iterator end() const + { + return impl.end(); + } + + std::size_t size() const + { + return impl.size(); + } + + alt_string_iter& operator+=(const char c) + { + impl += c; + return *this; + } + + std::string impl{}; +}; + +struct alt_string_data +{ + alt_string_data() = default; + alt_string_data(const char* cstr) + : impl(cstr) + {} + + void reserve(std::size_t s) + { + impl.reserve(s); + } + + void append(const char* p, std::size_t s) + { + impl.append(p, s); + } + + const char* data() const + { + return impl.data(); + } + + std::size_t size() const + { + return impl.size(); + } + + alt_string_data& operator+=(const char c) + { + impl += c; + return *this; + } + + std::string impl{}; +}; + void check_escaped(const char* original, const char* escaped = "", bool ensure_ascii = false); void check_escaped(const char* original, const char* escaped, const bool ensure_ascii) { @@ -110,4 +188,39 @@ TEST_CASE("convenience functions") CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); } + + SECTION("string concat") + { + using nlohmann::detail::concat; + + const char* expected = "Hello, world!"; + alt_string_iter hello_iter{"Hello, "}; + alt_string_data hello_data{"Hello, "}; + std::string world = "world"; + + SECTION("std::string") + { + std::string str1 = concat(hello_iter, world, '!'); + std::string str2 = concat(hello_data, world, '!'); + std::string str3 = concat("Hello, ", world, '!'); + + CHECK(str1 == expected); + CHECK(str2 == expected); + CHECK(str3 == expected); + } + + SECTION("alt_string_iter") + { + alt_string_iter str = concat(hello_iter, world, '!'); + + CHECK(str.impl == expected); + } + + SECTION("alt_string_data") + { + alt_string_data str = concat(hello_data, world, '!'); + + CHECK(str.impl == expected); + } + } } diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 8853c7aa6..7ec157db3 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -660,4 +660,54 @@ TEST_CASE("JSON pointers") CHECK(j[ptr] == j["object"]["/"]); CHECK(ptr.to_string() == "/object/~1"); } + + SECTION("equality comparison") + { + auto ptr1 = json::json_pointer("/foo/bar"); + auto ptr2 = json::json_pointer("/foo/bar"); + + CHECK(ptr1 == ptr2); + CHECK_FALSE(ptr1 != ptr2); + } + + SECTION("backwards compatibility and mixing") + { + json j = R"( + { + "foo": ["bar", "baz"] + } + )"_json; + + using nlohmann::ordered_json; + using json_ptr_str = nlohmann::json_pointer; + using json_ptr_j = nlohmann::json_pointer; + using json_ptr_oj = nlohmann::json_pointer; + + CHECK(std::is_same::value); + CHECK(std::is_same::value); + CHECK(std::is_same::value); + CHECK(std::is_same::value); + + json_ptr_str ptr{"/foo/0"}; + json_ptr_j ptr_j{"/foo/0"}; + json_ptr_oj ptr_oj{"/foo/0"}; + + CHECK(j.contains(ptr)); + CHECK(j.contains(ptr_j)); + CHECK(j.contains(ptr_oj)); + + CHECK(j.at(ptr) == j.at(ptr_j)); + CHECK(j.at(ptr) == j.at(ptr_oj)); + + CHECK(j[ptr] == j[ptr_j]); + CHECK(j[ptr] == j[ptr_oj]); + + CHECK(j.value(ptr, "x") == j.value(ptr_j, "x")); + CHECK(j.value(ptr, "x") == j.value(ptr_oj, "x")); + + CHECK(ptr == ptr_j); + CHECK(ptr == ptr_oj); + CHECK_FALSE(ptr != ptr_j); + CHECK_FALSE(ptr != ptr_oj); + } } diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index feb800e76..3ab9a2a2c 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -760,7 +760,6 @@ TEST_CASE("regression tests 2") { std::string p = "/root"; - // matching types json test1; test1[json::json_pointer(p)] = json::object(); CHECK(test1.dump() == "{\"root\":{}}"); @@ -769,10 +768,11 @@ TEST_CASE("regression tests 2") test2[ordered_json::json_pointer(p)] = json::object(); CHECK(test2.dump() == "{\"root\":{}}"); - // mixed type - the JSON Pointer is implicitly converted into a string "/root" + // json::json_pointer and ordered_json::json_pointer are the same type; behave as above ordered_json test3; test3[json::json_pointer(p)] = json::object(); - CHECK(test3.dump() == "{\"/root\":{}}"); + CHECK(std::is_same::value); + CHECK(test3.dump() == "{\"root\":{}}"); } SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type") From a94430615d8360272151f602b8c9eeb58509ecde Mon Sep 17 00:00:00 2001 From: JungHoon Lee <1620497@gmail.com> Date: Tue, 12 Apr 2022 22:58:06 +0900 Subject: [PATCH 044/110] Fix typo in basic_json documentation (#3439) --- doc/mkdocs/docs/api/basic_json/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mkdocs/docs/api/basic_json/index.md b/doc/mkdocs/docs/api/basic_json/index.md index eb90ac6ec..f8828dbd8 100644 --- a/doc/mkdocs/docs/api/basic_json/index.md +++ b/doc/mkdocs/docs/api/basic_json/index.md @@ -54,7 +54,7 @@ The class satisfies the following concept requirements: from an rvalue argument. - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): A JSON value can be copy-constructed from an lvalue expression. -- [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): A JSON value van be assigned from an +- [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): A JSON value can be assigned from an rvalue argument. - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): A JSON value can be copy-assigned from an lvalue expression. From 1a90c9463aa5691b4a1fd5b746fedfbecebe57ff Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 22 Apr 2022 14:21:16 +0200 Subject: [PATCH 045/110] Disable regression test for #3070 on GCC <8.4 (#3451) --- test/src/unit-regression2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 3ab9a2a2c..e034593ed 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -48,6 +48,7 @@ using ordered_json = nlohmann::ordered_json; #endif #if JSON_HAS_EXPERIMENTAL_FILESYSTEM +// JSON_HAS_CPP_17 (magic keyword; do not remove) #include namespace nlohmann::detail { @@ -61,7 +62,6 @@ namespace std_fs = std::filesystem; } // namespace nlohmann::detail #endif - #ifdef JSON_HAS_CPP_20 #include #endif @@ -793,8 +793,8 @@ TEST_CASE("regression tests 2") const auto j_path = j.get(); CHECK(j_path == text_path); -#ifndef _MSC_VER - // works everywhere but on MSVC +#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ == 8 && __GNUC_MINOR__ < 4) + // works everywhere but on MSVC and GCC <8.4 CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); #endif } From fcc36f99ba1afc7baebe24e0c7429d2039d32d99 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 24 Apr 2022 17:22:04 +0200 Subject: [PATCH 046/110] :arrow_up: cpplint 1.6.0 (#3454) --- third_party/cpplint/README.rst | 5 +---- third_party/cpplint/cpplint.py | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/third_party/cpplint/README.rst b/third_party/cpplint/README.rst index 18af13c00..9ff67c0eb 100644 --- a/third_party/cpplint/README.rst +++ b/third_party/cpplint/README.rst @@ -1,9 +1,6 @@ cpplint - static code checker for C++ ===================================== -.. image:: https://travis-ci.org/cpplint/cpplint.svg?branch=master - :target: https://travis-ci.org/cpplint/cpplint - .. image:: https://img.shields.io/pypi/v/cpplint.svg :target: https://pypi.python.org/pypi/cpplint @@ -62,7 +59,7 @@ The modifications in this fork are minor fixes and cosmetic changes, such as: * python 3 compatibility * more default file extensions * customizable file extensions with the --extensions argument -* continuous integration on travis +* continuous integration on github * support for recursive file discovery via the --recursive argument * support for excluding files via --exclude * JUnit XML output format diff --git a/third_party/cpplint/cpplint.py b/third_party/cpplint/cpplint.py index 3bf3441a3..6b78b308c 100755 --- a/third_party/cpplint/cpplint.py +++ b/third_party/cpplint/cpplint.py @@ -41,6 +41,11 @@ We do a small hack, which is to ignore //'s with "'s after them on the same line, but it is far from perfect (in either direction). """ +# cpplint predates fstrings +# pylint: disable=consider-using-f-string + +# pylint: disable=invalid-name + import codecs import copy import getopt @@ -59,7 +64,7 @@ import xml.etree.ElementTree # if empty, use defaults _valid_extensions = set([]) -__VERSION__ = '1.5.5' +__VERSION__ = '1.6.0' try: xrange # Python 2 @@ -1915,6 +1920,7 @@ class CleansedLines(object): self.raw_lines = lines self.num_lines = len(lines) self.lines_without_raw_strings = CleanseRawStrings(lines) + # # pylint: disable=consider-using-enumerate for linenum in range(len(self.lines_without_raw_strings)): self.lines.append(CleanseComments( self.lines_without_raw_strings[linenum])) @@ -5068,10 +5074,12 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): # # We also make an exception for Lua headers, which follow google # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - error(filename, linenum, 'build/include_subdir', 4, - 'Include the directory when naming .h files') + match = Match(r'#include\s*"([^/]+\.(.*))"', line) + if match: + if (IsHeaderExtension(match.group(2)) and + not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1))): + error(filename, linenum, 'build/include_subdir', 4, + 'Include the directory when naming header files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's @@ -6523,7 +6531,7 @@ def ProcessConfigOverrides(filename): continue try: - with open(cfg_file) as file_handle: + with open(cfg_file, encoding='utf-8') as file_handle: for line in file_handle: line, _, _ = line.partition('#') # Remove comments. if not line.strip(): From a6ee8bf9d94ef783f21f955d4125d5f3924d2c8e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 25 Apr 2022 22:40:45 +0200 Subject: [PATCH 047/110] Overwork documentation (#3444) * :memo: overwork macro documentation * :memo: address review comments * :wrench: add style check to Makefile * :see_no_evil: overwork .gitignore * :pushpin: Pygments 2.12.0 is broken * :pencil2: fix links * :children_crossing: adjust output to cppcheck * :memo: add titles to more admonitions * :pencil2: fix typos * :memo: document future behavior change --- .gitignore | 25 +- README.md | 16 +- ...lohmann_define_type_intrusive_explicit.cpp | 59 +++++ ...mann_define_type_intrusive_explicit.output | 2 + .../nlohmann_define_type_intrusive_macro.cpp | 47 ++++ ...lohmann_define_type_intrusive_macro.output | 2 + ...e_type_intrusive_with_default_explicit.cpp | 54 +++++ ...ype_intrusive_with_default_explicit.output | 2 + ...fine_type_intrusive_with_default_macro.cpp | 41 ++++ ...e_type_intrusive_with_default_macro.output | 2 + ...ann_define_type_non_intrusive_explicit.cpp | 52 +++++ ..._define_type_non_intrusive_explicit.output | 2 + ...ohmann_define_type_non_intrusive_macro.cpp | 40 ++++ ...ann_define_type_non_intrusive_macro.output | 2 + ...pe_non_intrusive_with_default_explicit.cpp | 52 +++++ ...non_intrusive_with_default_explicit.output | 2 + ..._type_non_intrusive_with_default_macro.cpp | 39 ++++ ...pe_non_intrusive_with_default_macro.output | 2 + doc/examples/nlohmann_json_serialize_enum.cpp | 59 +++++ .../nlohmann_json_serialize_enum.output | 3 + .../nlohmann_json_serialize_enum_2.cpp | 33 +++ .../nlohmann_json_serialize_enum_2.output | 3 + doc/mkdocs/Makefile | 19 +- doc/mkdocs/docs/api/basic_json/back.md | 4 +- doc/mkdocs/docs/api/basic_json/basic_json.md | 10 +- doc/mkdocs/docs/api/basic_json/front.md | 4 +- doc/mkdocs/docs/api/basic_json/get.md | 2 +- doc/mkdocs/docs/api/basic_json/get_ptr.md | 2 +- doc/mkdocs/docs/api/basic_json/get_ref.md | 4 +- doc/mkdocs/docs/api/basic_json/index.md | 2 +- .../docs/api/basic_json/is_discarded.md | 4 +- doc/mkdocs/docs/api/basic_json/items.md | 2 +- doc/mkdocs/docs/api/basic_json/meta.md | 4 + .../api/basic_json/object_comparator_t.md | 2 +- doc/mkdocs/docs/api/basic_json/operator[].md | 5 +- .../docs/api/basic_json/operator_ValueType.md | 47 ++-- doc/mkdocs/docs/api/basic_json/value.md | 2 +- doc/mkdocs/docs/api/json_pointer/index.md | 12 +- doc/mkdocs/docs/api/macros/index.md | 49 ++-- doc/mkdocs/docs/api/macros/json_assert.md | 81 ++++++- .../docs/api/macros/json_diagnostics.md | 67 ++++++ doc/mkdocs/docs/api/macros/json_has_cpp_11.md | 28 +++ .../docs/api/macros/json_has_filesystem.md | 30 +++ doc/mkdocs/docs/api/macros/json_no_io.md | 21 ++ .../docs/api/macros/json_noexception.md | 32 +++ .../macros/json_skip_library_version_check.md | 37 +++ .../json_skip_unsupported_compiler_check.md | 20 ++ doc/mkdocs/docs/api/macros/json_throw_user.md | 75 ++++++ .../macros/json_use_implicit_conversions.md | 56 +++++ .../macros/nlohmann_define_type_intrusive.md | 124 ++++++++++ .../nlohmann_define_type_non_intrusive.md | 124 ++++++++++ .../macros/nlohmann_json_serialize_enum.md | 84 +++++++ .../api/macros/nlohmann_json_version_major.md | 23 ++ doc/mkdocs/docs/features/arbitrary_types.md | 17 +- doc/mkdocs/docs/features/assertions.md | 104 +++++++++ .../element_access/unchecked_access.md | 12 +- doc/mkdocs/docs/features/enum_conversion.md | 11 +- doc/mkdocs/docs/features/macros.md | 220 +++--------------- .../docs/features/types/number_handling.md | 2 +- doc/mkdocs/docs/home/exceptions.md | 7 +- doc/mkdocs/mkdocs.yml | 27 ++- doc/mkdocs/requirements.txt | 77 +++--- doc/mkdocs/scripts/check_structure.py | 63 +++-- 63 files changed, 1689 insertions(+), 366 deletions(-) create mode 100644 doc/examples/nlohmann_define_type_intrusive_explicit.cpp create mode 100644 doc/examples/nlohmann_define_type_intrusive_explicit.output create mode 100644 doc/examples/nlohmann_define_type_intrusive_macro.cpp create mode 100644 doc/examples/nlohmann_define_type_intrusive_macro.output create mode 100644 doc/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp create mode 100644 doc/examples/nlohmann_define_type_intrusive_with_default_explicit.output create mode 100644 doc/examples/nlohmann_define_type_intrusive_with_default_macro.cpp create mode 100644 doc/examples/nlohmann_define_type_intrusive_with_default_macro.output create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_explicit.cpp create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_explicit.output create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_macro.cpp create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_macro.output create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp create mode 100644 doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.output create mode 100644 doc/examples/nlohmann_json_serialize_enum.cpp create mode 100644 doc/examples/nlohmann_json_serialize_enum.output create mode 100644 doc/examples/nlohmann_json_serialize_enum_2.cpp create mode 100644 doc/examples/nlohmann_json_serialize_enum_2.output create mode 100644 doc/mkdocs/docs/api/macros/json_diagnostics.md create mode 100644 doc/mkdocs/docs/api/macros/json_has_cpp_11.md create mode 100644 doc/mkdocs/docs/api/macros/json_has_filesystem.md create mode 100644 doc/mkdocs/docs/api/macros/json_no_io.md create mode 100644 doc/mkdocs/docs/api/macros/json_noexception.md create mode 100644 doc/mkdocs/docs/api/macros/json_skip_library_version_check.md create mode 100644 doc/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md create mode 100644 doc/mkdocs/docs/api/macros/json_throw_user.md create mode 100644 doc/mkdocs/docs/api/macros/json_use_implicit_conversions.md create mode 100644 doc/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md create mode 100644 doc/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md create mode 100644 doc/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md create mode 100644 doc/mkdocs/docs/api/macros/nlohmann_json_version_major.md create mode 100644 doc/mkdocs/docs/features/assertions.md mode change 100644 => 100755 doc/mkdocs/scripts/check_structure.py diff --git a/.gitignore b/.gitignore index a13ad89a5..e4f92117b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,21 @@ -json_unit -json_benchmarks -json_benchmarks_simple -fuzz-testing - *.dSYM *.o *.gcno *.gcda - -build -build_coverage -clang_analyze_build - -benchmarks/files/numbers/*.json +.DS_Store .wsjcpp-logs/* .wsjcpp/* -.idea +/.idea /cmake-build-* -test/test-* /.vs -doc/html -doc/mkdocs/venv/ -doc/mkdocs/docs/examples -doc/mkdocs/site -doc/mkdocs/docs/__pycache__/ +/doc/mkdocs/docs/examples/ +/doc/mkdocs/docs/__pycache__/ +/doc/mkdocs/site/ +/doc/mkdocs/venv/ /doc/docset/JSON_for_Modern_C++.docset/ /doc/docset/JSON_for_Modern_C++.tgz +/doc/mkdocs/docs/images/json.gif diff --git a/README.md b/README.md index 9d9bd2c91..20e7c48ea 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/t ## 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/api/basic_json/emplace/)). 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)). +Beside the examples below, you may want to check the [documentation](https://json.nlohmann.me/) where each function contains a separate code example (e.g., check out [`emplace()`](https://json.nlohmann.me/api/basic_json/emplace/)). 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)). ### JSON as first-class data type @@ -162,7 +162,7 @@ json j2 = { }; ``` -Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array()`](https://nlohmann.github.io/json/api/basic_json/array/) and [`json::object()`](https://nlohmann.github.io/json/api/basic_json/object/) will help: +Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array()`](https://json.nlohmann.me/api/basic_json/array/) and [`json::object()`](https://json.nlohmann.me/api/basic_json/object/) will help: ```cpp // a way to express the empty array [] @@ -197,7 +197,7 @@ auto j2 = R"( Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object. -The above example can also be expressed explicitly using [`json::parse()`](https://nlohmann.github.io/json/api/basic_json/parse/): +The above example can also be expressed explicitly using [`json::parse()`](https://json.nlohmann.me/api/basic_json/parse/): ```cpp // parse explicitly @@ -240,9 +240,9 @@ std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) -* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/api/basic_json/at/) 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. +* In function `from_json`, use function [`at()`](https://json.nlohmann.me/api/basic_json/at/) 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 @@ -1647,7 +1647,7 @@ The library supports **Unicode input** as follows: - [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs. -- When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. +- When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. - To store wide strings (e.g., `std::wstring`), you need to convert them to a UTF-8 encoded `std::string` before, see [an example](https://json.nlohmann.me/home/faq/#wide-string-handling). ### Comments in JSON @@ -1682,7 +1682,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/api/basic_json/operator%5B%5D/) 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/api/basic_json/at/). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. +- 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://json.nlohmann.me/api/basic_json/operator%5B%5D/) 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://json.nlohmann.me/api/basic_json/at/). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. - As the exact number type 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. Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). diff --git a/doc/examples/nlohmann_define_type_intrusive_explicit.cpp b/doc/examples/nlohmann_define_type_intrusive_explicit.cpp new file mode 100644 index 000000000..fb7701dec --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_explicit.cpp @@ -0,0 +1,59 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +class person +{ + private: + std::string name = "John Doe"; + std::string address = "123 Fake St"; + int age = -1; + + public: + person() = default; + person(std::string name_, std::string address_, int age_) + : name(std::move(name_)), address(std::move(address_)), age(age_) + {} + + friend void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) + { + nlohmann_json_j["name"] = nlohmann_json_t.name; + nlohmann_json_j["address"] = nlohmann_json_t.address; + nlohmann_json_j["age"] = nlohmann_json_t.age; + } + + friend void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) + { + nlohmann_json_t.name = nlohmann_json_j.at("name"); + nlohmann_json_t.address = nlohmann_json_j.at("address"); + nlohmann_json_t.age = nlohmann_json_j.at("age"); + } +}; +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + try + { + auto p3 = j3.get(); + } + catch (json::exception& e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } +} diff --git a/doc/examples/nlohmann_define_type_intrusive_explicit.output b/doc/examples/nlohmann_define_type_intrusive_explicit.output new file mode 100644 index 000000000..37f4eb414 --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_explicit.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +deserialization failed: [json.exception.out_of_range.403] key 'age' not found diff --git a/doc/examples/nlohmann_define_type_intrusive_macro.cpp b/doc/examples/nlohmann_define_type_intrusive_macro.cpp new file mode 100644 index 000000000..ce292659a --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_macro.cpp @@ -0,0 +1,47 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +class person +{ + private: + std::string name = "John Doe"; + std::string address = "123 Fake St"; + int age = -1; + + public: + person() = default; + person(std::string name_, std::string address_, int age_) + : name(std::move(name_)), address(std::move(address_)), age(age_) + {} + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(person, name, address, age) +}; +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + try + { + auto p3 = j3.get(); + } + catch (json::exception& e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } +} diff --git a/doc/examples/nlohmann_define_type_intrusive_macro.output b/doc/examples/nlohmann_define_type_intrusive_macro.output new file mode 100644 index 000000000..37f4eb414 --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_macro.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +deserialization failed: [json.exception.out_of_range.403] key 'age' not found diff --git a/doc/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp b/doc/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp new file mode 100644 index 000000000..621ed1452 --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_with_default_explicit.cpp @@ -0,0 +1,54 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +class person +{ + private: + std::string name = "John Doe"; + std::string address = "123 Fake St"; + int age = -1; + + public: + person() = default; + person(std::string name_, std::string address_, int age_) + : name(std::move(name_)), address(std::move(address_)), age(age_) + {} + + friend void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) + { + nlohmann_json_j["name"] = nlohmann_json_t.name; + nlohmann_json_j["address"] = nlohmann_json_t.address; + nlohmann_json_j["age"] = nlohmann_json_t.age; + } + + friend void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) + { + person nlohmann_json_default_obj; + nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name); + nlohmann_json_t.address = nlohmann_json_j.value("address", nlohmann_json_default_obj.address); + nlohmann_json_t.age = nlohmann_json_j.value("age", nlohmann_json_default_obj.age); + } +}; +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + auto p3 = j3.get(); + std::cout << "roundtrip: " << json(p3) << std::endl; +} diff --git a/doc/examples/nlohmann_define_type_intrusive_with_default_explicit.output b/doc/examples/nlohmann_define_type_intrusive_with_default_explicit.output new file mode 100644 index 000000000..1a255f65c --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_with_default_explicit.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +roundtrip: {"address":"742 Evergreen Terrace","age":-1,"name":"Maggie Simpson"} diff --git a/doc/examples/nlohmann_define_type_intrusive_with_default_macro.cpp b/doc/examples/nlohmann_define_type_intrusive_with_default_macro.cpp new file mode 100644 index 000000000..7851f526e --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_with_default_macro.cpp @@ -0,0 +1,41 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +class person +{ + private: + std::string name = "John Doe"; + std::string address = "123 Fake St"; + int age = -1; + + public: + person() = default; + person(std::string name_, std::string address_, int age_) + : name(std::move(name_)), address(std::move(address_)), age(age_) + {} + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person, name, address, age) +}; +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + auto p3 = j3.get(); + std::cout << "roundtrip: " << json(p3) << std::endl; +} diff --git a/doc/examples/nlohmann_define_type_intrusive_with_default_macro.output b/doc/examples/nlohmann_define_type_intrusive_with_default_macro.output new file mode 100644 index 000000000..1a255f65c --- /dev/null +++ b/doc/examples/nlohmann_define_type_intrusive_with_default_macro.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +roundtrip: {"address":"742 Evergreen Terrace","age":-1,"name":"Maggie Simpson"} diff --git a/doc/examples/nlohmann_define_type_non_intrusive_explicit.cpp b/doc/examples/nlohmann_define_type_non_intrusive_explicit.cpp new file mode 100644 index 000000000..b9d30dd8f --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_explicit.cpp @@ -0,0 +1,52 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +struct person +{ + std::string name; + std::string address; + int age; +}; + +void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) +{ + nlohmann_json_j["name"] = nlohmann_json_t.name; + nlohmann_json_j["address"] = nlohmann_json_t.address; + nlohmann_json_j["age"] = nlohmann_json_t.age; +} + +void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) +{ + nlohmann_json_t.name = nlohmann_json_j.at("name"); + nlohmann_json_t.address = nlohmann_json_j.at("address"); + nlohmann_json_t.age = nlohmann_json_j.at("age"); +} +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + try + { + auto p3 = j3.get(); + } + catch (json::exception& e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } +} diff --git a/doc/examples/nlohmann_define_type_non_intrusive_explicit.output b/doc/examples/nlohmann_define_type_non_intrusive_explicit.output new file mode 100644 index 000000000..37f4eb414 --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_explicit.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +deserialization failed: [json.exception.out_of_range.403] key 'age' not found diff --git a/doc/examples/nlohmann_define_type_non_intrusive_macro.cpp b/doc/examples/nlohmann_define_type_non_intrusive_macro.cpp new file mode 100644 index 000000000..b073ef615 --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_macro.cpp @@ -0,0 +1,40 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +struct person +{ + std::string name; + std::string address; + int age; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age) +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + try + { + auto p3 = j3.get(); + } + catch (json::exception& e) + { + std::cout << "deserialization failed: " << e.what() << std::endl; + } +} diff --git a/doc/examples/nlohmann_define_type_non_intrusive_macro.output b/doc/examples/nlohmann_define_type_non_intrusive_macro.output new file mode 100644 index 000000000..37f4eb414 --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_macro.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +deserialization failed: [json.exception.out_of_range.403] key 'age' not found diff --git a/doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp b/doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp new file mode 100644 index 000000000..21967b638 --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp @@ -0,0 +1,52 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +struct person +{ + std::string name = "John Doe"; + std::string address = "123 Fake St"; + int age = -1; + + person() = default; + person(std::string name_, std::string address_, int age_) + : name(std::move(name_)), address(std::move(address_)), age(age_) + {} +}; + +void to_json(nlohmann::json& nlohmann_json_j, const person& nlohmann_json_t) +{ + nlohmann_json_j["name"] = nlohmann_json_t.name; + nlohmann_json_j["address"] = nlohmann_json_t.address; + nlohmann_json_j["age"] = nlohmann_json_t.age; +} + +void from_json(const nlohmann::json& nlohmann_json_j, person& nlohmann_json_t) +{ + person nlohmann_json_default_obj; + nlohmann_json_t.name = nlohmann_json_j.value("name", nlohmann_json_default_obj.name); + nlohmann_json_t.address = nlohmann_json_j.value("address", nlohmann_json_default_obj.address); + nlohmann_json_t.age = nlohmann_json_j.value("age", nlohmann_json_default_obj.age); +} +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + auto p3 = j3.get(); + std::cout << "roundtrip: " << json(p3) << std::endl; +} diff --git a/doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output b/doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output new file mode 100644 index 000000000..1a255f65c --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_with_default_explicit.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +roundtrip: {"address":"742 Evergreen Terrace","age":-1,"name":"Maggie Simpson"} diff --git a/doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp b/doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp new file mode 100644 index 000000000..470fed69c --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp @@ -0,0 +1,39 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +struct person +{ + std::string name = "John Doe"; + std::string address = "123 Fake St"; + int age = -1; + + person() = default; + person(std::string name_, std::string address_, int age_) + : name(std::move(name_)), address(std::move(address_)), age(age_) + {} +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person, name, address, age) +} // namespace ns + +int main() +{ + ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; + + // serialization: person -> json + json j = p; + std::cout << "serialization: " << j << std::endl; + + // deserialization: json -> person + json j2 = R"({"address": "742 Evergreen Terrace", "age": 40, "name": "Homer Simpson"})"_json; + auto p2 = j2.get(); + + // incomplete deserialization: + json j3 = R"({"address": "742 Evergreen Terrace", "name": "Maggie Simpson"})"_json; + auto p3 = j3.get(); + std::cout << "roundtrip: " << json(p3) << std::endl; +} diff --git a/doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.output b/doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.output new file mode 100644 index 000000000..1a255f65c --- /dev/null +++ b/doc/examples/nlohmann_define_type_non_intrusive_with_default_macro.output @@ -0,0 +1,2 @@ +serialization: {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"} +roundtrip: {"address":"742 Evergreen Terrace","age":-1,"name":"Maggie Simpson"} diff --git a/doc/examples/nlohmann_json_serialize_enum.cpp b/doc/examples/nlohmann_json_serialize_enum.cpp new file mode 100644 index 000000000..f9e472c64 --- /dev/null +++ b/doc/examples/nlohmann_json_serialize_enum.cpp @@ -0,0 +1,59 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +enum TaskState +{ + TS_STOPPED, + TS_RUNNING, + TS_COMPLETED, + TS_INVALID = -1 +}; + +NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, +{ + { TS_INVALID, nullptr }, + { TS_STOPPED, "stopped" }, + { TS_RUNNING, "running" }, + { TS_COMPLETED, "completed" } +}) + +enum class Color +{ + red, green, blue, unknown +}; + +NLOHMANN_JSON_SERIALIZE_ENUM(Color, +{ + { Color::unknown, "unknown" }, { Color::red, "red" }, + { Color::green, "green" }, { Color::blue, "blue" } +}) +} // namespace ns + +int main() +{ + // serialization + json j_stopped = ns::TS_STOPPED; + json j_red = ns::Color::red; + std::cout << "ns::TS_STOPPED -> " << j_stopped + << ", ns::Color::red -> " << j_red << std::endl; + + // deserialization + json j_running = "running"; + json j_blue = "blue"; + auto running = j_running.get(); + auto blue = j_blue.get(); + std::cout << j_running << " -> " << running + << ", " << j_blue << " -> " << static_cast(blue) << std::endl; + + // deserializing undefined JSON value to enum + // (where the first map entry above is the default) + json j_pi = 3.14; + auto invalid = j_pi.get(); + auto unknown = j_pi.get(); + std::cout << j_pi << " -> " << invalid << ", " + << j_pi << " -> " << static_cast(unknown) << std::endl; +} diff --git a/doc/examples/nlohmann_json_serialize_enum.output b/doc/examples/nlohmann_json_serialize_enum.output new file mode 100644 index 000000000..f512563dd --- /dev/null +++ b/doc/examples/nlohmann_json_serialize_enum.output @@ -0,0 +1,3 @@ +ns::TS_STOPPED -> "stopped", ns::Color::red -> "red" +"running" -> 1, "blue" -> 2 +3.14 -> -1, 3.14 -> 3 diff --git a/doc/examples/nlohmann_json_serialize_enum_2.cpp b/doc/examples/nlohmann_json_serialize_enum_2.cpp new file mode 100644 index 000000000..fd27226ca --- /dev/null +++ b/doc/examples/nlohmann_json_serialize_enum_2.cpp @@ -0,0 +1,33 @@ +#include +#include + +using json = nlohmann::json; + +namespace ns +{ +enum class Color +{ + red, green, blue, unknown +}; + +NLOHMANN_JSON_SERIALIZE_ENUM(Color, +{ + { Color::unknown, "unknown" }, { Color::red, "red" }, + { Color::green, "green" }, { Color::blue, "blue" }, + { Color::red, "rot" } // a second conversion for Color::red +}) +} + +int main() +{ + // serialization + json j_red = ns::Color::red; + std::cout << static_cast(ns::Color::red) << " -> " << j_red << std::endl; + + // deserialization + json j_rot = "rot"; + auto rot = j_rot.get(); + auto red = j_red.get(); + std::cout << j_rot << " -> " << static_cast(rot) << std::endl; + std::cout << j_red << " -> " << static_cast(red) << std::endl; +} diff --git a/doc/examples/nlohmann_json_serialize_enum_2.output b/doc/examples/nlohmann_json_serialize_enum_2.output new file mode 100644 index 000000000..5dec31b4a --- /dev/null +++ b/doc/examples/nlohmann_json_serialize_enum_2.output @@ -0,0 +1,3 @@ +0 -> "red" +"rot" -> 0 +"red" -> 0 diff --git a/doc/mkdocs/Makefile b/doc/mkdocs/Makefile index 3d3d5e7c6..3f894d098 100644 --- a/doc/mkdocs/Makefile +++ b/doc/mkdocs/Makefile @@ -1,18 +1,21 @@ # serve the site locally -serve: prepare_files +serve: prepare_files style_check venv/bin/mkdocs serve -build: prepare_files +serve_dirty: prepare_files style_check + venv/bin/mkdocs serve --dirtyreload + +build: prepare_files style_check venv/bin/mkdocs build -# create files that are not versioned inside the mkdocs folder +# create files that are not versioned inside the mkdocs folder (images, examples) prepare_files: clean - # create subfolders mkdir docs/examples - # copy images - cp -vr ../json.gif docs/images - # copy examples - cp -vr ../examples/*.cpp ../examples/*.output docs/examples + cp -r ../json.gif docs/images + cp -r ../examples/*.cpp ../examples/*.output docs/examples + +style_check: + @cd docs ; python3 ../scripts/check_structure.py # clean subfolders clean: diff --git a/doc/mkdocs/docs/api/basic_json/back.md b/doc/mkdocs/docs/api/basic_json/back.md index 96e1dec75..1a715284d 100644 --- a/doc/mkdocs/docs/api/basic_json/back.md +++ b/doc/mkdocs/docs/api/basic_json/back.md @@ -35,9 +35,9 @@ Constant. ## Notes -!!! danger +!!! info "Precondition" - Calling `back` on an empty array or object is undefined behavior and is **guarded by an assertion**! + The array or object must not be empty. Calling `back` on an empty array or object yields undefined behavior. ## Examples diff --git a/doc/mkdocs/docs/api/basic_json/basic_json.md b/doc/mkdocs/docs/api/basic_json/basic_json.md index ab3fea470..afa3901d1 100644 --- a/doc/mkdocs/docs/api/basic_json/basic_json.md +++ b/doc/mkdocs/docs/api/basic_json/basic_json.md @@ -250,17 +250,15 @@ basic_json(basic_json&& other) noexcept; !!! info "Preconditions" - - Iterators `first` and `last` must be initialized. **This precondition is enforced with an assertion (see - warning).** If assertions are switched off, a violation of this precondition yields undefined behavior. + - Iterators `first` and `last` must be initialized. **This precondition is enforced with a + [runtime assertion](../../features/assertions.md). - Range `[first, last)` is valid. Usually, this precondition cannot be checked efficiently. Only certain edge cases are detected; see the description of the exceptions above. A violation of this precondition yields undefined behavior. - !!! warning + !!! danger "Runtime assertion" - A precondition is enforced with a runtime assertion that will result in calling `std::abort` if this - precondition is not met. Assertions can be disabled by defining `NDEBUG` at compile time. See - for more information. + A precondition is enforced with a [runtime assertion](../../features/assertions.md). - Overload 8: diff --git a/doc/mkdocs/docs/api/basic_json/front.md b/doc/mkdocs/docs/api/basic_json/front.md index 909f0b59b..e258c36a0 100644 --- a/doc/mkdocs/docs/api/basic_json/front.md +++ b/doc/mkdocs/docs/api/basic_json/front.md @@ -28,9 +28,9 @@ Constant. ## Notes -!!! danger +!!! info "Precondition" - Calling `front` on an empty array or object is undefined behavior and is **guarded by an assertion**! + The array or object must not be empty. Calling `front` on an empty array or object yields undefined behavior. ## Examples diff --git a/doc/mkdocs/docs/api/basic_json/get.md b/doc/mkdocs/docs/api/basic_json/get.md index 0a0bc3bab..96fc221da 100644 --- a/doc/mkdocs/docs/api/basic_json/get.md +++ b/doc/mkdocs/docs/api/basic_json/get.md @@ -90,7 +90,7 @@ Depends on what `json_serializer` `from_json()` method throws ## Notes -!!! warning +!!! danger "Undefined behavior" Writing data to the pointee (overload 3) of the result yields an undefined state. diff --git a/doc/mkdocs/docs/api/basic_json/get_ptr.md b/doc/mkdocs/docs/api/basic_json/get_ptr.md index 72517cd7e..2441e1156 100644 --- a/doc/mkdocs/docs/api/basic_json/get_ptr.md +++ b/doc/mkdocs/docs/api/basic_json/get_ptr.md @@ -33,7 +33,7 @@ Constant. ## Notes -!!! warning +!!! danger "Undefined behavior" Writing data to the pointee of the result yields an undefined state. diff --git a/doc/mkdocs/docs/api/basic_json/get_ref.md b/doc/mkdocs/docs/api/basic_json/get_ref.md index 1140836e4..b1219742c 100644 --- a/doc/mkdocs/docs/api/basic_json/get_ref.md +++ b/doc/mkdocs/docs/api/basic_json/get_ref.md @@ -16,7 +16,7 @@ Implicit reference access to the internally stored JSON value. No copies are mad : reference type; must be a reference to [`array_t`](array_t.md), [`object_t`](object_t.md), [`string_t`](string_t.md), [`boolean_t`](boolean_t.md), [`number_integer_t`](number_integer_t.md), or [`number_unsigned_t`](number_unsigned_t.md), [`number_float_t`](number_float_t.md), or [`binary_t`](binary_t.md). - Enforced by static assertion. + Enforced by a static assertion. ## Return value @@ -38,7 +38,7 @@ Constant. ## Notes -!!! warning +!!! danger "Undefined behavior" Writing data to the referee of the result yields an undefined state. diff --git a/doc/mkdocs/docs/api/basic_json/index.md b/doc/mkdocs/docs/api/basic_json/index.md index f8828dbd8..286fea2b4 100644 --- a/doc/mkdocs/docs/api/basic_json/index.md +++ b/doc/mkdocs/docs/api/basic_json/index.md @@ -292,7 +292,7 @@ Access to the JSON value - [**std::hash<basic_json>**](std_hash.md) - return a hash value for a JSON object - [**std::swap<basic_json>**](std_swap.md) - exchanges the values of two JSON objects -## Example +## Examples ??? example diff --git a/doc/mkdocs/docs/api/basic_json/is_discarded.md b/doc/mkdocs/docs/api/basic_json/is_discarded.md index 6de31c937..663cbf889 100644 --- a/doc/mkdocs/docs/api/basic_json/is_discarded.md +++ b/doc/mkdocs/docs/api/basic_json/is_discarded.md @@ -24,7 +24,7 @@ Constant. ## Notes -!!! note +!!! note "Comparisons" Discarded values are never compared equal with [`operator==`](operator_eq.md). That is, checking whether a JSON value `j` is discarded will only work via: @@ -41,7 +41,7 @@ Constant. will always be `#!cpp false`. -!!! note +!!! note "Removal during parsing with callback functions" When a value is discarded by a callback function (see [`parser_callback_t`](parser_callback_t.md)) during parsing, then it is removed when it is part of a structured value. For instance, if the second value of an array is discarded, diff --git a/doc/mkdocs/docs/api/basic_json/items.md b/doc/mkdocs/docs/api/basic_json/items.md index b388824f9..0b34ddcba 100644 --- a/doc/mkdocs/docs/api/basic_json/items.md +++ b/doc/mkdocs/docs/api/basic_json/items.md @@ -63,7 +63,7 @@ Constant. When iterating over an array, `key()` will return the index of the element as string (see example). For primitive types (e.g., numbers), `key()` returns an empty string. -!!! warning +!!! danger "Lifetime issues" Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exceeds the iteration. See for more information. diff --git a/doc/mkdocs/docs/api/basic_json/meta.md b/doc/mkdocs/docs/api/basic_json/meta.md index e2b312e0c..87767e4d5 100644 --- a/doc/mkdocs/docs/api/basic_json/meta.md +++ b/doc/mkdocs/docs/api/basic_json/meta.md @@ -45,6 +45,10 @@ Constant. --8<-- "examples/meta.output" ``` +## See also + +- [**NLOHMANN_JSON_VERSION_MAJOR**/**NLOHMANN_JSON_VERSION_MINOR**/**NLOHMANN_JSON_VERSION_PATCH**](../macros/nlohmann_json_version_major.md) - library version information + ## Version history - Added in version 2.1.0. diff --git a/doc/mkdocs/docs/api/basic_json/object_comparator_t.md b/doc/mkdocs/docs/api/basic_json/object_comparator_t.md index 44509a94b..e2bc79d05 100644 --- a/doc/mkdocs/docs/api/basic_json/object_comparator_t.md +++ b/doc/mkdocs/docs/api/basic_json/object_comparator_t.md @@ -8,7 +8,7 @@ using object_comparator_t = std::less<>; // since C++14 The comparator used in [`object_t`](object_t.md). -When C++14 is detected, a transparent com parator is used which, when combined with perfect forwarding on find() and +When C++14 is detected, a transparent comparator is used which, when combined with perfect forwarding on find() and count() calls, prevents unnecessary string construction. ## Version history diff --git a/doc/mkdocs/docs/api/basic_json/operator[].md b/doc/mkdocs/docs/api/basic_json/operator[].md index 5b6512a21..cc9eae7f3 100644 --- a/doc/mkdocs/docs/api/basic_json/operator[].md +++ b/doc/mkdocs/docs/api/basic_json/operator[].md @@ -74,10 +74,11 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Notes -!!! danger +!!! danger "Undefined behavior and runtime assertions" 1. If the element with key `idx` does not exist, the behavior is undefined. - 2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by an assertion**! + 2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a + [runtime assertion](../../features/assertions.md)**! 1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In diff --git a/doc/mkdocs/docs/api/basic_json/operator_ValueType.md b/doc/mkdocs/docs/api/basic_json/operator_ValueType.md index 1eec13553..787588781 100644 --- a/doc/mkdocs/docs/api/basic_json/operator_ValueType.md +++ b/doc/mkdocs/docs/api/basic_json/operator_ValueType.md @@ -27,33 +27,44 @@ Linear in the size of the JSON value. ## Notes -By default `JSON_EXPLICIT` defined to the empty string, so the signature is: +!!! note "Definition of `JSON_EXPLICIT`" -```cpp -template -operator ValueType() const; -``` + By default `JSON_EXPLICIT` is defined to the empty string, so the signature is: + + ```cpp + template + operator ValueType() const; + ``` + + If [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) is set to `0`, + `JSON_EXPLICIT` is defined to `#!cpp explicit`: -If [`JSON_USE_IMPLICIT_CONVERSIONS`](../../features/macros.md#json_use_implicit_conversions) is set to `0`, -`JSON_EXPLICIT` is defined to `#!cpp explicit`: + ```cpp + template + explicit operator ValueType() const; + ``` + + That is, implicit conversions can be switched off by defining + [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) to `0`. -```cpp -template -explicit operator ValueType() const; -``` +!!! info "Future behavior change" + + Implicit conversions will be switched off by default in the next major release of the library. That is, + `JSON_EXPLICIT` will be set to `#!cpp explicit` by default. + + You can prepare existing code by already defining + [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) to `0` and replace any implicit + conversions with calls to [`get`](../basic_json/get.md). -That is, implicit conversions can be switched off by defining -[`JSON_USE_IMPLICIT_CONVERSIONS`](../../features/macros.md#json_use_implicit_conversions) to `0`. ## Examples ??? example - The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers, (2) A JSON array can be converted to a standard - `std::vector`, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`. + The example below shows several conversions from JSON values to other types. There are a few things to note: (1) + Floating-point numbers can be converted to integers, (2) A JSON array can be converted to a standard + `std::vector`, (3) A JSON object can be converted to C++ associative containers such as + `std::unordered_map`. ```cpp --8<-- "examples/operator__ValueType.cpp" diff --git a/doc/mkdocs/docs/api/basic_json/value.md b/doc/mkdocs/docs/api/basic_json/value.md index 0b4f1cc19..1844c41fb 100644 --- a/doc/mkdocs/docs/api/basic_json/value.md +++ b/doc/mkdocs/docs/api/basic_json/value.md @@ -36,7 +36,7 @@ ValueType value(const json_pointer& ptr, } ``` -!!! note +!!! note "Differences to `at` and `operator[]`" - Unlike [`at`](at.md), this function does not throw if the given `key`/`ptr` was not found. - Unlike [`operator[]`](operator[].md), this function does not implicitly add an element to the position defined by diff --git a/doc/mkdocs/docs/api/json_pointer/index.md b/doc/mkdocs/docs/api/json_pointer/index.md index 3504c9ff7..dca9c382c 100644 --- a/doc/mkdocs/docs/api/json_pointer/index.md +++ b/doc/mkdocs/docs/api/json_pointer/index.md @@ -16,7 +16,13 @@ are the base for JSON patches. ## Notes -For backwards compatibility `RefStringType` may also be a specialization of [`basic_json`](../basic_json/index.md) in which case `string_t` will be deduced as [`basic_json::string_t`](../basic_json/string_t.md). This feature is deprecated and may be removed in a future major version. +For backwards compatibility `RefStringType` may also be a specialization of [`basic_json`](../basic_json/index.md) in +which case `string_t` will be deduced as [`basic_json::string_t`](../basic_json/string_t.md). This feature is deprecated +and may be removed in a future major version. + +## Member types + +- [**string_t**](string_t.md) - the string type used for the reference tokens ## Member functions @@ -31,10 +37,6 @@ For backwards compatibility `RefStringType` may also be a specialization of [`ba - [**push_back**](push_back.md) - append an unescaped token at the end of the pointer - [**empty**](empty.md) - return whether pointer points to the root document -## Member types - -- [**string_t**](string_t.md) - the string type used for the reference tokens - ## See also - [operator""_json_pointer](../basic_json/operator_literal_json_pointer.md) - user-defined string literal for JSON pointers diff --git a/doc/mkdocs/docs/api/macros/index.md b/doc/mkdocs/docs/api/macros/index.md index 4c723c6a6..56924da44 100644 --- a/doc/mkdocs/docs/api/macros/index.md +++ b/doc/mkdocs/docs/api/macros/index.md @@ -1,22 +1,37 @@ # Macros -!!! note +Some aspects of the library can be configured by defining preprocessor macros **before** including the `json.hpp` +header. See also the [macro overview page](../../features/macros.md). - This page is under construction. See the [macro overview page](../../features/macros.md) until then. +## Runtime assertions -Some aspects of the library can be configured by defining preprocessor macros before including the `json.hpp` header. +- [**JSON_ASSERT(x)**](json_assert.md) - control behavior of runtime assertions -- [`JSON_ASSERT(x)`](json_assert.md) -- `JSON_CATCH_USER(exception)` -- `JSON_DIAGNOSTICS` -- `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, `JSON_HAS_CPP_20` -- `JSON_NOEXCEPTION` -- `JSON_NO_IO` -- `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK` -- `JSON_THROW_USER(exception)` -- `JSON_TRY_USER` -- `JSON_USE_IMPLICIT_CONVERSIONS` -- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)` -- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)` -- `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` -- `NLOHMANN_JSON_VERSION_MAJOR`, `NLOHMANN_JSON_VERSION_MINOR`, `NLOHMANN_JSON_VERSION_PATCH` +## Exceptions + +- [**JSON_CATCH_USER(exception)**
**JSON_THROW_USER(exception)**
**JSON_TRY_USER**](json_throw_user.md) - control exceptions +- [**JSON_DIAGNOSTICS**](json_diagnostics.md) - control extended diagnostics +- [**JSON_NOEXCEPTION**](json_noexception.md) - switch off exceptions + +## Language support + +- [**JSON_HAS_CPP_11**
**JSON_HAS_CPP_14**
**JSON_HAS_CPP_17**
**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard +- [**JSON_HAS_FILESYSTEM**
**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support +- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers +- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers + +## Library version + +- [**JSON_SKIP_LIBRARY_VERSION_CHECK**](json_skip_library_version_check.md) - skip library version check +- [**NLOHMANN_JSON_VERSION_MAJOR**
**NLOHMANN_JSON_VERSION_MINOR**
**NLOHMANN_JSON_VERSION_PATCH**](nlohmann_json_version_major.md) - library version information + +## Type conversions + +- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions + +## Serialization/deserialization macros + +- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)**](nlohmann_define_type_intrusive.md) - serialization/deserialization of types _with_ access to private variables +- [**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)**
**NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)**](nlohmann_define_type_non_intrusive.md) - serialization/deserialization of types _without_ access to private variables +- [**NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)**](nlohmann_json_serialize_enum.md) - serialization/deserialization of enum + types diff --git a/doc/mkdocs/docs/api/macros/json_assert.md b/doc/mkdocs/docs/api/macros/json_assert.md index 63d4ae076..a093341a1 100644 --- a/doc/mkdocs/docs/api/macros/json_assert.md +++ b/doc/mkdocs/docs/api/macros/json_assert.md @@ -1,11 +1,84 @@ -# JSON_ASSERT(x) +# JSON_ASSERT ```cpp -JSON_ASSERT(x) +#define JSON_ASSERT(x) /* value */ ``` -## Default implementation +This macro controls which code is executed for [runtime assertions](../../features/assertions.md) of the library. + +## Parameters + +`x` (in) +: expression of scalar type + +## Default definition + +The default value is [`#!cpp assert(x)`](https://en.cppreference.com/w/cpp/error/assert). ```cpp -assert(x); +#define JSON_ASSERT(x) assert(x) ``` + +Therefore, assertions can be switched off by defining `NDEBUG`. + +## Notes + +- The library uses numerous assertions to guarantee invariants and to abort in case of otherwise undefined behavior + (e.g., when calling [operator[]](../basic_json/operator%5B%5D.md) with a missing object key on a `const` object). See + page [runtime assertions](../../features/assertions.md) for more information. +- Defining the macro to code that does not call `std::abort` may leave the library in an undefined state. +- The macro is undefined outside the library. + +## Examples + +??? example "Example 1: default behavior" + + The following code will trigger an assertion at runtime: + + ```cpp + #include + + using json = nlohmann::json; + + int main() + { + const json j = {{"key", "value"}}; + auto v = j["missing"]; + } + ``` + + Output: + + ``` + Assertion failed: (m_value.object->find(key) != m_value.object->end()), function operator[], file json.hpp, line 2144. + ``` + +??? example "Example 2: user-defined behavior" + + The assertion reporting can be changed by defining `JSON_ASSERT(x)` differently. + + ```cpp + #include + #include + #define JSON_ASSERT(x) if(!(x)){fprintf(stderr, "assertion error in %s\n", __FUNCTION__); std::abort();} + + #include + + using json = nlohmann::json; + + int main() + { + const json j = {{"key", "value"}}; + auto v = j["missing"]; + } + ``` + + Output: + + ``` + assertion error in operator[] + ``` + +## Version history + +- Added in version 3.9.0. diff --git a/doc/mkdocs/docs/api/macros/json_diagnostics.md b/doc/mkdocs/docs/api/macros/json_diagnostics.md new file mode 100644 index 000000000..d42025092 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_diagnostics.md @@ -0,0 +1,67 @@ +# JSON_DIAGNOSTICS + +```cpp +#define JSON_DIAGNOSTICS /* value */ +``` + +This macro enables [extended diagnostics for exception messages](../../home/exceptions.md#extended-diagnostic-messages). +Possible values are `1` to enable or `0` to disable (default). + +When enabled, exception messages contain a [JSON Pointer](../json_pointer/json_pointer.md) to the JSON value that +triggered the exception. Note that enabling this macro increases the size of every JSON value by one pointer and adds +some runtime overhead. + +The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets +`JSON_DIAGNOSTICS` accordingly. + +## Default definition + +The default value is `0` (extended diagnostics are switched off). + +```cpp +#define JSON_DIAGNOSTICS 0 +``` + +When the macro is not defined, the library will define it to its default value. + +## Notes + +!!! danger "ABI incompatibility" + + As this macro changes the definition of the `basic_json` object, it MUST be defined in the same way globally, even + across different compilation units: `basic_json` objects with differently defined `JSON_DIAGNOSTICS` macros are + not compatible! + +## Examples + +??? example "Example 1: default behavior" + + ```cpp + --8<-- "examples/diagnostics_standard.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostics_standard.output" + ``` + + This exception can be hard to debug if storing the value `#!c "12"` and accessing it is further apart. + +??? example "Example 2: extended diagnostic messages" + + ```cpp + --8<-- "examples/diagnostics_extended.cpp" + ``` + + Output: + + ``` + --8<-- "examples/diagnostics_extended.output" + ``` + + Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type. + +## Version history + +- Added in version 3.10.0. diff --git a/doc/mkdocs/docs/api/macros/json_has_cpp_11.md b/doc/mkdocs/docs/api/macros/json_has_cpp_11.md new file mode 100644 index 000000000..3bee84324 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_has_cpp_11.md @@ -0,0 +1,28 @@ +# JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20 + +```cpp +#define JSON_HAS_CPP_11 +#define JSON_HAS_CPP_14 +#define JSON_HAS_CPP_17 +#define JSON_HAS_CPP_20 +``` + +The library targets C++11, but also supports some features introduced in later C++ versions (e.g., `std::string_view` +support for C++17). For these new features, the library implements some preprocessor checks to determine the C++ +standard. By defining any of these symbols, the internal check is overridden and the provided C++ version is +unconditionally assumed. This can be helpful for compilers that only implement parts of the standard and would be +detected incorrectly. + +## Default definition + +The default value is detected based on preprocessor macros such as `#!cpp __cplusplus`, `#!cpp _HAS_CXX17`, or +`#!cpp _MSVC_LANG`. + +## Notes + +- `#!cpp JSON_HAS_CPP_11` is always defined. +- All macros are undefined outside the library. + +## Version history + +- Added in version 3.10.5. diff --git a/doc/mkdocs/docs/api/macros/json_has_filesystem.md b/doc/mkdocs/docs/api/macros/json_has_filesystem.md new file mode 100644 index 000000000..160ad8c24 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_has_filesystem.md @@ -0,0 +1,30 @@ +# JSON_HAS_FILESYSTEM / JSON_HAS_EXPERIMENTAL_FILESYSTEM + +```cpp +#define JSON_HAS_FILESYSTEM /* value */ +#define JSON_HAS_EXPERIMENTAL_FILESYSTEM /* value */ +``` + +When compiling with C++17, the library provides conversions from and to +[`std::filesystem::path`](https://en.cppreference.com/w/cpp/filesystem/path). As compiler support for filesystem is +limited, the library tries to detect whether +[``/`std::filesystem`](https://en.cppreference.com/w/cpp/header/filesystem) (`JSON_HAS_FILESYSTEM`) or +[``/`std::experimental::filesystem`](https://en.cppreference.com/w/cpp/header/experimental/filesystem) +(`JSON_HAS_EXPERIMENTAL_FILESYSTEM`) should be used. To override the built-in check, define `JSON_HAS_FILESYSTEM` or +`JSON_HAS_EXPERIMENTAL_FILESYSTEM` to `1`. + +## Default definition + +The default value is detected based on the preprocessor macros `#!cpp __cpp_lib_filesystem`, +`#!cpp __cpp_lib_experimental_filesystem`, `#!cpp __has_include()`, or +`#!cpp __has_include()`. + +## Notes + +- Note that older compilers or older versions of libstd++ also require the library `stdc++fs` to be linked to for + filesystem support. +- Both macros are undefined outside the library. + +## Version history + +- Added in version 3.10.5. diff --git a/doc/mkdocs/docs/api/macros/json_no_io.md b/doc/mkdocs/docs/api/macros/json_no_io.md new file mode 100644 index 000000000..10ae24c8a --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_no_io.md @@ -0,0 +1,21 @@ +# JSON_NO_IO + +```cpp +#define JSON_NO_IO +``` + +When defined, headers ``, ``, ``, ``, and `` are not included and parse functions +relying on these headers are excluded. This is relevant for environments where these I/O functions are disallowed for +security reasons (e.g., Intel Software Guard Extensions (SGX)). + +## Default definition + +By default, `#!cpp JSON_NO_IO` is not defined. + +```cpp +#undef JSON_NO_IO +``` + +## Version history + +- Added in version 3.10.0. diff --git a/doc/mkdocs/docs/api/macros/json_noexception.md b/doc/mkdocs/docs/api/macros/json_noexception.md new file mode 100644 index 000000000..0f32b63e9 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_noexception.md @@ -0,0 +1,32 @@ +# JSON_NOEXCEPTION + +```cpp +#define 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`. + +## Default definition + +By default, the macro is not defined. + +```cpp +#undef JSON_NOEXCEPTION +``` + +## Notes + +The explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not +available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). + +## See also + +- [Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for more information how to switch off exceptions + +## Version history + +Added in version 2.1.0. diff --git a/doc/mkdocs/docs/api/macros/json_skip_library_version_check.md b/doc/mkdocs/docs/api/macros/json_skip_library_version_check.md new file mode 100644 index 000000000..c9a743c18 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_skip_library_version_check.md @@ -0,0 +1,37 @@ +# JSON_SKIP_LIBRARY_VERSION_CHECK + +```cpp +#define JSON_SKIP_LIBRARY_VERSION_CHECK +``` + +When defined, the library will not create a compiler warning when a different version of the library was already +included. + +## Default definition + +By default, the macro is not defined. + +```cpp +#undef JSON_SKIP_LIBRARY_VERSION_CHECK +``` + +## Notes + +!!! danger "ABI compatibility" + + Mixing different library versions in the same code can be a problem as the different versions may not be ABI + compatible. + +## Examples + +!!! example + + The following warning will be shown in case a different version of the library was already included: + + ``` + Already included a different version of the library! + ``` + +## Version history + +Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md b/doc/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md new file mode 100644 index 000000000..c58d0ea85 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_skip_unsupported_compiler_check.md @@ -0,0 +1,20 @@ +# JSON_SKIP_UNSUPPORTED_COMPILER_CHECK + +```cpp +#define 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. + +## Default definition + +By default, the macro is not defined. + +```cpp +#undef JSON_SKIP_UNSUPPORTED_COMPILER_CHECK +``` + +## Version history + +Added in version 3.2.0. diff --git a/doc/mkdocs/docs/api/macros/json_throw_user.md b/doc/mkdocs/docs/api/macros/json_throw_user.md new file mode 100644 index 000000000..e10db90e4 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_throw_user.md @@ -0,0 +1,75 @@ +# JSON_CATCH_USER, JSON_THROW_USER, JSON_TRY_USER + +```cpp +// (1) +#define JSON_CATCH_USER(exception) /* value */ +// (2) +#define JSON_THROW_USER(exception) /* value */ +// (3) +#define JSON_TRY_USER /* value */ +``` + +Controls how exceptions are handled by the library. + +1. This macro overrides [`#!cpp catch`](https://en.cppreference.com/w/cpp/language/try_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. +2. 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. +3. This macro overrides `#!cpp try` calls inside the library. It has no arguments and is always followed by a scope. + +## Parameters + +`exception` (in) +: an exception type + +## Default definition + +By default, the macros map to their respective C++ keywords: + +```cpp +#define JSON_CATCH_USER(exception) catch(exception) +#define JSON_THROW_USER(exception) throw exception +#define JSON_TRY_USER try +``` + +When exceptions are switched off, the `#!cpp try` block is executed unconditionally, and throwing exceptions is +replaced by calling [`std::abort`](https://en.cppreference.com/w/cpp/utility/program/abort) to make reaching the +`#!cpp throw` branch abort the process. + +```cpp +#define JSON_THROW_USER(exception) std::abort() +#define JSON_TRY_USER if (true) +#define JSON_CATCH_USER(exception) if (false) +``` + +## Examples + +??? 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 + ``` + +## See also + +- [Switch off exceptions](../../home/exceptions.md#switch-off-exceptions) for more information how to switch off exceptions +- [JSON_NOEXCEPTION](JSON_NOEXCEPTION) - switch off exceptions + +## Version history + +- Added in version 3.1.0. diff --git a/doc/mkdocs/docs/api/macros/json_use_implicit_conversions.md b/doc/mkdocs/docs/api/macros/json_use_implicit_conversions.md new file mode 100644 index 000000000..3ee81b061 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/json_use_implicit_conversions.md @@ -0,0 +1,56 @@ +# JSON_USE_IMPLICIT_CONVERSIONS + +```cpp +#define JSON_USE_IMPLICIT_CONVERSIONS /* value */ +``` + +When defined to `0`, implicit conversions are switched off. By default, implicit conversions are switched on. The +value directly affects [`operator ValueType`](../basic_json/operator_ValueType.md). + +Implicit conversions can also be controlled with the CMake option `JSON_ImplicitConversions` (`ON` by default) which +sets `JSON_USE_IMPLICIT_CONVERSIONS` accordingly. + +## Default definition + +By default, implicit conversions are enabled. + +```cpp +#define JSON_USE_IMPLICIT_CONVERSIONS 1 +``` + +## Notes + +!!! info "Future behavior change" + + Implicit conversions will be switched off by default in the next major release of the library. + + You can prepare existing code by already defining `JSON_USE_IMPLICIT_CONVERSIONS` to `0` and replace any implicit + conversions with calls to [`get`](../basic_json/get.md). + +## Examples + +??? example + + This is an example for an implicit conversion: + + ```cpp + json j = "Hello, world!"; + std::string s = j; + ``` + + When `JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`, the code above does no longer compile. Instead, it must be + written like this: + + ```cpp + json j = "Hello, world!"; + auto s = j.get(); + ``` + +## See also + +- [**operator ValueType**](../basic_json/operator_ValueType.md) - get a value (implicit) +- [**get**](../basic_json/get.md) - get a value (explicit) + +## Version history + +- Added in version 3.9.0. diff --git a/doc/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md b/doc/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md new file mode 100644 index 000000000..7269ef081 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/nlohmann_define_type_intrusive.md @@ -0,0 +1,124 @@ +# NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT + +```cpp +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...) // (1) +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...) // (2) +``` + +These macros can be used to simplify the serialization/deserialization of types if you want to use a JSON object as +serialization and want to use the member variable names as object keys in that object. The macro is to be defined +**inside** the class/struct to create code for. +Unlike [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](nlohmann_define_type_non_intrusive.md), it can access private members. +The first parameter is the name of the class/struct, and all remaining parameters name the members. + +1. Will use [`at`](../basic_json/at.md) during deserialization and will throw + [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a key is missing in the JSON object. +2. Will use [`value`](../basic_json/value.md) during deserialization and fall back to the default value for the + respective type of the member variable if a key in the JSON object is missing. The generated `from_json()` function + default constructs an object and uses its values as the defaults when calling the `value` function. + +## Parameters + +`type` (in) +: name of the type (class, struct) to serialize/deserialize + +`member` (in) +: name of the member variable to serialize/deserialize; up to 64 members can be given as comma-separated list + +## Default definition + +The macros add two friend functions to the class which take care of the serialization and deserialization: + +```cpp +friend void to_json(nlohmann::json&, const type&); +friend void from_json(const nlohmann::json&, type&); +``` + +See examples below for the concrete generated code. + +## Notes + +!!! info "Prerequisites" + + 1. The type `type` must be default constructible. See [How can I use `get()` for non-default constructible/non-copyable types?](../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types) + for how to overcome this limitation. + 2. The macro must be used inside the type (class/struct). + +!!! warning "Implementation limits" + + - The current implementation is limited to at most 64 member variables. If you want to serialize/deserialize types + with more than 64 member variables, you need to define the `to_json`/`from_json` functions manually. + - The macros only work for the [`nlohmann::json`](../json.md) type; other specializations such as + [`nlohmann::ordered_json`](../ordered_json.md) are currently unsupported. + +## Examples + +??? example "Example (1): NLOHMANN_DEFINE_TYPE_INTRUSIVE" + + Consider the following complete example: + + ```cpp hl_lines="21" + --8<-- "examples/nlohmann_define_type_intrusive_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_type_intrusive_macro.output" + ``` + + Notes: + + - `ns::person` is default-constructible. This is a requirement for using the macro. + - `ns::person` has private member variables. This makes `NLOHMANN_DEFINE_TYPE_INTRUSIVE` applicable, but not + `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. + - The macro `NLOHMANN_DEFINE_TYPE_INTRUSIVE` is used _inside_ the class. + - A missing key "age" in the deserialization yields an exception. To fall back to the default value, + `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` can be used. + + The macro is equivalent to: + + ```cpp hl_lines="21 22 23 24 25 26 27 28 29 30 31 32 33" + --8<-- "examples/nlohmann_define_type_intrusive_explicit.cpp" + ``` + +??? example "Example (2): NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT" + + Consider the following complete example: + + ```cpp hl_lines="21" + --8<-- "examples/nlohmann_define_type_intrusive_with_default_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_type_intrusive_with_default_macro.output" + ``` + + Notes: + + - `ns::person` is default-constructible. This is a requirement for using the macro. + - `ns::person` has private member variables. This makes `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` applicable, + but not `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`. + - The macro `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` is used _inside_ the class. + - A missing key "age" in the deserialization does not yield an exception. Instead, the default value `-1` is used. + + The macro is equivalent to: + + ```cpp hl_lines="21 22 23 24 25 26 27 28 29 30 31 32 33 34" + --8<-- "examples/nlohmann_define_type_intrusive_with_default_explicit.cpp" + ``` + + Note how a default-initialized `person` object is used in the `from_json` to fill missing values. + +## See also + +- [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE / NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT](nlohmann_define_type_non_intrusive.md) + for a similar macro that can be defined _outside_ the type. +- [Arbitrary Types Conversions](../../features/arbitrary_types.md) for an overview. + +## Version history + +1. Added in version 3.9.0. +2. Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md b/doc/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md new file mode 100644 index 000000000..7ed5a6b43 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/nlohmann_define_type_non_intrusive.md @@ -0,0 +1,124 @@ +# NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT + +```cpp +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...) // (1) +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...) // (2) +``` + +These macros can be used to simplify the serialization/deserialization of types if you want to use a JSON object as +serialization and want to use the member variable names as object keys in that object. The macro is to be defined +**outside** the class/struct to create code for, but **inside** its namespace. +Unlike [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](nlohmann_define_type_intrusive.md), it **cannot** access private members. +The first parameter is the name of the class/struct, and all remaining parameters name the members. + +1. Will use [`at`](../basic_json/at.md) during deserialization and will throw + [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a key is missing in the JSON object. +2. Will use [`value`](../basic_json/value.md) during deserialization and fall back to the default value for the + respective type of the member variable if a key in the JSON object is missing. The generated `from_json()` function + default constructs an object and uses its values as the defaults when calling the `value` function. + +## Parameters + +`type` (in) +: name of the type (class, struct) to serialize/deserialize + +`member` (in) +: name of the (public) member variable to serialize/deserialize; up to 64 members can be given as comma-separated list + +## Default definition + +The macros add two functions to the namespace which take care of the serialization and deserialization: + +```cpp +void to_json(nlohmann::json&, const type&); +void from_json(const nlohmann::json&, type&); +``` + +See examples below for the concrete generated code. + +## Notes + +!!! info "Prerequisites" + + 1. The type `type` must be default constructible. See [How can I use `get()` for non-default constructible/non-copyable types?](../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types) + for how to overcome this limitation. + 2. The macro must be used outside the type (class/struct). + 3. The passed members must be public. + +!!! warning "Implementation limits" + + - The current implementation is limited to at most 64 member variables. If you want to serialize/deserialize types + with more than 64 member variables, you need to define the `to_json`/`from_json` functions manually. + - The macros only work for the [`nlohmann::json`](../json.md) type; other specializations such as + [`nlohmann::ordered_json`](../ordered_json.md) are currently unsupported. + +## Examples + +??? example "Example (1): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE" + + Consider the following complete example: + + ```cpp hl_lines="15" + --8<-- "examples/nlohmann_define_type_non_intrusive_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_type_non_intrusive_macro.output" + ``` + + Notes: + + - `ns::person` is default-constructible. This is a requirement for using the macro. + - `ns::person` has only public member variables. This makes `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` applicable. + - The macro `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` is used _outside_ the class, but _inside_ its namespace `ns`. + - A missing key "age" in the deserialization yields an exception. To fall back to the default value, + `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT` can be used. + + The macro is equivalent to: + + ```cpp hl_lines="15 16 17 18 19 20 21 22 23 24 25 26 27" + --8<-- "examples/nlohmann_define_type_non_intrusive_explicit.cpp" + ``` + +??? example "Example (2): NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT" + + Consider the following complete example: + + ```cpp hl_lines="20" + --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_macro.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_macro.output" + ``` + + Notes: + + - `ns::person` is default-constructible. This is a requirement for using the macro. + - `ns::person` has only public member variables. This makes `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT` + applicable. + - The macro `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT` is used _outside_ the class, but _inside_ its namespace `ns`. + - A missing key "age" in the deserialization does not yield an exception. Instead, the default value `-1` is used. + + The macro is equivalent to: + + ```cpp hl_lines="20 21 22 23 24 25 26 27 28 29 30 31 32 33" + --8<-- "examples/nlohmann_define_type_non_intrusive_with_default_explicit.cpp" + ``` + + Note how a default-initialized `person` object is used in the `from_json` to fill missing values. + +## See also + +- [NLOHMANN_DEFINE_TYPE_INTRUSIVE / NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT](nlohmann_define_type_intrusive.md) + for a similar macro that can be defined _inside_ the type. +- [Arbitrary Types Conversions](../../features/arbitrary_types.md) for an overview. + +## Version history + +1. Added in version 3.9.0. +2. Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md b/doc/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md new file mode 100644 index 000000000..7b0f89802 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/nlohmann_json_serialize_enum.md @@ -0,0 +1,84 @@ +# NLOHMANN_JSON_SERIALIZE_ENUM + +```cpp +#define NLOHMANN_JSON_SERIALIZE_ENUM(type, conversion...) +``` + +By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an +enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be +undefined or a different enum value than was originally intended. + +The `NLOHMANN_JSON_SERIALIZE_ENUM` allows to define a user-defined serialization for every enumerator. + +## Parameters + +`type` (in) +: name of the enum to serialize/deserialize + +`conversion` (in) +: a pair of an enumerator and a JSON serialization; arbitrary pairs can can be given as comma-separated list + +## Default definition + +The macros add two friend functions to the class which take care of the serialization and deserialization: + +```cpp +template +inline void to_json(BasicJsonType& j, const type& e); +template +inline void from_json(const BasicJsonType& j, type& e); +``` + +## Notes + +!!! info "Prerequisites" + + The macro must be used inside the namespace of the enum. + +!!! important "Important notes" + + - When using [`get()`](../basic_json/get.md), undefined JSON values will default to the first specified + conversion. Select this default pair carefully. See example 1 below. + - If an enum or JSON value is specified in multiple conversions, the first matching conversion from the top of the + list will be returned when converting to or from JSON. See example 2 below. + +## Examples + +??? example "Example 1: Basic usage" + + The example shows how `NLOHMANN_JSON_SERIALIZE_ENUM` can be used to serialize/deserialize both classical enums and + C++11 enum classes: + + ```cpp hl_lines="16 17 18 19 20 21 22 29 30 31 32 33" + --8<-- "examples/nlohmann_json_serialize_enum.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_json_serialize_enum.output" + ``` + +??? example "Example 2: Multiple conversions for one enumerator" + + The example shows how to use multiple conversions for a single enumerator. In the example, `Color::red` will always + be *serialized* to `"red"`, because the first occurring conversion. The second conversion, however, offers an + alternative *deserialization* from `"rot"` to `Color::red`. + + ```cpp hl_lines="17" + --8<-- "examples/nlohmann_json_serialize_enum_2.cpp" + ``` + + Output: + + ```json + --8<-- "examples/nlohmann_json_serialize_enum_2.output" + ``` + +## See also + +- [Specializing enum conversion](../../features/enum_conversion.md) + +## Version history + +Added in version 3.4.0. diff --git a/doc/mkdocs/docs/api/macros/nlohmann_json_version_major.md b/doc/mkdocs/docs/api/macros/nlohmann_json_version_major.md new file mode 100644 index 000000000..826785292 --- /dev/null +++ b/doc/mkdocs/docs/api/macros/nlohmann_json_version_major.md @@ -0,0 +1,23 @@ +# NLOHMANN_JSON_VERSION_MAJOR, NLOHMANN_JSON_VERSION_MINOR, NLOHMANN_JSON_VERSION_PATCH + +```cpp +#define NLOHMANN_JSON_VERSION_MAJOR /* value */ +#define NLOHMANN_JSON_VERSION_MINOR /* value */ +#define NLOHMANN_JSON_VERSION_PATCH /* value */ +``` + +These macros are defined by the library and contain the version numbers according to +[Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). + +## Default definition + +The macros are defined according to the current library version. + +## See also + +- [meta](../basic_json/meta.md) - returns version information on the library +- [JSON_SKIP_LIBRARY_VERSION_CHECK](json_skip_library_version_check.md) - skip library version check + +## Version history + +- Added in version 3.1.0. diff --git a/doc/mkdocs/docs/features/arbitrary_types.md b/doc/mkdocs/docs/features/arbitrary_types.md index 67e8a4554..49a541ef5 100644 --- a/doc/mkdocs/docs/features/arbitrary_types.md +++ b/doc/mkdocs/docs/features/arbitrary_types.md @@ -77,7 +77,7 @@ Some important things: * Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). * Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. * When using `get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) -* 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. +* In function `from_json`, use function [`at()`](../api/basic_json/at.md) 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. @@ -87,16 +87,19 @@ If you just want to serialize/deserialize some structs, the `to_json`/`from_json There are four 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 the namespace of the class/struct to create code for. It will throw an exception in `from_json()` due to a missing value in the JSON object. -- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside the namespace of the class/struct to create code for. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. -- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. It will throw an exception in `from_json()` due to a missing value in the JSON object. -- `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)` is to be defined inside the class/struct to create code for. This macro can also access private members. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. +- [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_non_intrusive.md) is to be defined inside the namespace of the class/struct to create code for. It will throw an exception in `from_json()` due to a missing value in the JSON object. +- [`NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_non_intrusive.md) is to be defined inside the namespace of the class/struct to create code for. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. +- [`NLOHMANN_DEFINE_TYPE_INTRUSIVE(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_intrusive.md) is to be defined inside the class/struct to create code for. This macro can also access private members. It will throw an exception in `from_json()` due to a missing value in the JSON object. +- [`NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(name, member1, member2, ...)`](../api/macros/nlohmann_define_type_intrusive.md) is to be defined inside the class/struct to create code for. This macro can also access private members. It will not throw an exception in `from_json()` due to a missing value in the JSON object, but fills in values from object which is default-constructed by the type. In all macros, the first parameter is the name of the class/struct, and all remaining parameters name the members. You can read more docs about them starting from [here](macros.md#nlohmann_define_type_intrusivetype-member). -!!! note +!!! info "Implementation limits" - At most 64 member variables can be passed to these macros. + - The current macro implementations are limited to at most 64 member variables. If you want to serialize/deserialize + types with more than 64 member variables, you need to define the `to_json`/`from_json` functions manually. + - The macros only work for the [`nlohmann::json`](../api/json.md) type; other specializations such as + [`nlohmann::ordered_json`](../api/ordered_json.md) are currently unsupported. ??? example diff --git a/doc/mkdocs/docs/features/assertions.md b/doc/mkdocs/docs/features/assertions.md new file mode 100644 index 000000000..a0b118722 --- /dev/null +++ b/doc/mkdocs/docs/features/assertions.md @@ -0,0 +1,104 @@ +# Runtime Assertions + +The code contains numerous debug assertions to ensure class invariants are valid or to detect undefined behavior. +Whereas the former class invariants are nothing to be concerned of, the latter checks for undefined behavior are to +detect bugs in client code. + +## Switch off runtime assertions + +Runtime assertions can be switched off by defining the preprocessor macro `NDEBUG` (see the +[documentation of assert](https://en.cppreference.com/w/cpp/error/assert)) which is the default for release builds. + +## Change assertion behavior + +The behavior of runtime assertions can be changes by defining macro [`JSON_ASSERT(x)`](../api/macros/json_assert.md) +before including the `json.hpp` header. + +## Function with runtime assertions + +### Unchecked object access to a const value + +Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for objects. Whereas a missing +key is added in case of non-const objects, accessing a const object with a missing key is undefined behavior (think of a +dereferenced null pointer) and yields a runtime assertion. + +If you are not sure whether an element in an object exists, use checked access with the +[`at` function](../api/basic_json/at.md) or call the [`contains` function](../api/basic_json/contains.md) before. + +See also the documentation on [element access](element_access/index.md). + +??? example "Example 1: Missing object key" + + The following code will trigger an assertion at runtime: + + ```cpp + #include + + using json = nlohmann::json; + + int main() + { + const json j = {{"key", "value"}}; + auto v = j["missing"]; + } + ``` + + Output: + + ``` + Assertion failed: (m_value.object->find(key) != m_value.object->end()), function operator[], file json.hpp, line 2144. + ``` + +### Constructing from an uninitialized iterator range + +Constructing a JSON value from an iterator range (see [constructor](../api/basic_json/basic_json.md)) with an +uninitialized iterator is undefined behavior and yields a runtime assertion. + +??? example "Example 2: Uninitialized iterator range" + + The following code will trigger an assertion at runtime: + + ```cpp + #include + + using json = nlohmann::json; + + int main() + { + json::iterator it1, it2; + json j(it1, it2); + } + ``` + + Output: + + ``` + Assertion failed: (m_object != nullptr), function operator++, file iter_impl.hpp, line 368. + ``` + +### Operations on uninitialized iterators + +Any operation on uninitialized iterators (i.e., iterators that are not associated with any JSON value) is undefined +behavior and yields a runtime assertion. + +??? example "Example 3: Uninitialized iterator" + + The following code will trigger an assertion at runtime: + + ```cpp + #include + + using json = nlohmann::json; + + int main() + { + json::iterator it; + ++it; + } + ``` + + Output: + + ``` + Assertion failed: (m_object != nullptr), function operator++, file iter_impl.hpp, line 368. + ``` diff --git a/doc/mkdocs/docs/features/element_access/unchecked_access.md b/doc/mkdocs/docs/features/element_access/unchecked_access.md index bb7228777..4fdef35c7 100644 --- a/doc/mkdocs/docs/features/element_access/unchecked_access.md +++ b/doc/mkdocs/docs/features/element_access/unchecked_access.md @@ -94,9 +94,9 @@ When accessing an invalid index (i.e., an index greater than or equal to the arr ## Summary -| scenario | non-const value | const value | -|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------| -| access to existing object key | reference to existing value is returned | const reference to existing value is returned | -| access to valid array index | reference to existing value is returned | const reference to existing value is returned | -| access to non-existing object key | reference to newly inserted `#!json null` value is returned | **undefined behavior**; assertion in debug mode | -| access to invalid array index | reference to newly inserted `#!json null` value is returned; any index between previous maximal index and passed index are filled with `#!json null` | **undefined behavior**; assertion in debug mode | +| scenario | non-const value | const value | +|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| +| access to existing object key | reference to existing value is returned | const reference to existing value is returned | +| access to valid array index | reference to existing value is returned | const reference to existing value is returned | +| access to non-existing object key | reference to newly inserted `#!json null` value is returned | **undefined behavior**; [runtime assertion](../assertions.md) in debug mode | +| access to invalid array index | reference to newly inserted `#!json null` value is returned; any index between previous maximal index and passed index are filled with `#!json null` | **undefined behavior**; [runtime assertion](../assertions.md) in debug mode | diff --git a/doc/mkdocs/docs/features/enum_conversion.md b/doc/mkdocs/docs/features/enum_conversion.md index 8f474a702..7e61f67bb 100644 --- a/doc/mkdocs/docs/features/enum_conversion.md +++ b/doc/mkdocs/docs/features/enum_conversion.md @@ -24,8 +24,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { }) ``` -The `NLOHMANN_JSON_SERIALIZE_ENUM()` macro declares a set of `to_json()` / `from_json()` functions for type `TaskState` -while avoiding repetition and boilerplate serialization code. +The [`NLOHMANN_JSON_SERIALIZE_ENUM()` macro](../api/macros/nlohmann_json_serialize_enum.md) declares a set of +`to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. ## Usage @@ -45,10 +45,11 @@ assert(jPi.get() == TS_INVALID ); ## Notes -Just as in [Arbitrary Type Conversions](#arbitrary-types-conversions) above, +Just as in [Arbitrary Type Conversions](arbitrary_types.md) above, -- `NLOHMANN_JSON_SERIALIZE_ENUM()` MUST be declared in your enum type's namespace (which can be the global namespace), - or the library will not be able to locate it, and it will default to integer serialization. +- [`NLOHMANN_JSON_SERIALIZE_ENUM()`](../api/macros/nlohmann_json_serialize_enum.md) MUST be declared in your enum type's + namespace (which can be the global namespace), or the library will not be able to locate it, and it will default to + integer serialization. - It MUST be available (e.g., proper headers must be included) everywhere you use the conversions. Other Important points: diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index 576ec935d..e04a426a5 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -1,47 +1,19 @@ # Supported Macros Some aspects of the library can be configured by defining preprocessor macros before including the `json.hpp` header. +See also the [API documentation for macros](../api/macros/index.md) for examples and more information. ## `JSON_ASSERT(x)` -This macro controls which code is executed for runtime assertions of the libraries. +This macro controls which code is executed for [runtime assertions](assertions.md) of the library. -!!! info "Default behavior" - - The default value is [`#!cpp assert(x)`](https://en.cppreference.com/w/cpp/error/assert). - - ```cpp - #define JSON_ASSERT(x) assert(x) - ``` - -The macro was introduced in version 3.9.0. +See [full documentation of `JSON_ASSERT(x)`](../api/macros/json_assert.md). ## `JSON_CATCH_USER(exception)` This macro overrides [`#!cpp catch`](https://en.cppreference.com/w/cpp/language/try_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. - -!!! info "Default behavior" - - When exceptions are enabled, the default value is - [`#!cpp catch(exception)`](https://en.cppreference.com/w/cpp/language/try_catch). - - ```cpp - #define JSON_CATCH_USER(exception) catch(exception) - ``` - - When exceptions are switched off by the compiler, the default value is `#!cpp if (false)` to make the catch block - unreachable. - - ```cpp - #define JSON_CATCH_USER(exception) if (false) - ``` - -The macro was introduced in version 3.1.0. +See [full documentation of `JSON_CATCH_USER(exception)`](../api/macros/json_throw_user.md). ## `JSON_DIAGNOSTICS` @@ -55,19 +27,7 @@ that enabling this macro increases the size of every JSON value by one pointer a The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. -!!! warning - - As this macro changes the definition of the `basic_json` object, it MUST be defined in the same way globally, even - across different compilation units; DO NOT link together code compiled with different definitions of - `JSON_DIAGNOSTICS` as this is a violation of the One Definition Rule and will cause undefined behaviour. - -!!! info "Default behavior" - - ```cpp - #define JSON_DIAGNOSTICS 0 - ``` - -The macro was introduced in version 3.10.0. +See [full documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). ## `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, `JSON_HAS_CPP_20` @@ -77,12 +37,7 @@ standard. By defining any of these symbols, the internal check is overridden and unconditionally assumed. This can be helpful for compilers that only implement parts of the standard and would be detected incorrectly. -!!! info "Default behavior" - - The default value is detected based on the preprocessor macros `#!cpp __cplusplus`, `#!cpp _HAS_CXX17`, or - `#!cpp _MSVC_LANG`. - -The macros were introduced in version 3.10.5. +See [full documentation of `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, and `JSON_HAS_CPP_20`](../api/macros/json_has_cpp_11.md). ## `JSON_HAS_FILESYSTEM`, `JSON_HAS_EXPERIMENTAL_FILESYSTEM` @@ -91,37 +46,13 @@ for filesystem is limited, the library tries to detect whether ``/`s or ``/`std::experimental::filesystem` (`JSON_HAS_EXPERIMENTAL_FILESYSTEM`) should be used. To override the built-in check, define `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERIMENTAL_FILESYSTEM` to `1`. -!!! info "Default behavior" - - The default value is detected based on the preprocessor macros `#!cpp __cpp_lib_filesystem`, - `#!cpp __cpp_lib_experimental_filesystem`, `#!cpp __has_include()`, or - `#!cpp __has_include()`. - -Note that older compilers or older versions of libstd++ also require the library `stdc++fs` to be linked to for -filesystem support. - -The macros were introduced in version 3.10.5. +See [full documentation of `JSON_HAS_FILESYSTEM` and `JSON_HAS_EXPERIMENTAL_FILESYSTEM`](../api/macros/json_has_filesystem.md). ## `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()`. +Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. -!!! info "Default behavior" - - By default, the macro is not defined. - - ```cpp - #undef JSON_NOEXCEPTION - ``` - -The same effect is achieved by setting the compiler flag `-fno-exceptions`. - -Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not -available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). - -The macro was introduced in version 2.1.0. +See [full documentation of `JSON_NOEXCEPTION`](../api/macros/json_noexception.md). ## `JSON_NO_IO` @@ -129,128 +60,39 @@ When defined, headers ``, ``, ``, ``, and `(); - ``` - -Implicit conversions can also be controlled with the CMake option `JSON_ImplicitConversions` (`ON` by default) which -sets `JSON_USE_IMPLICIT_CONVERSIONS` accordingly. - -!!! info "Default behavior" - - ```cpp - #define JSON_USE_IMPLICIT_CONVERSIONS 1 - ``` - -The macro was introduced in version 3.9.0. +See [full documentation of `JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md). ## `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)` @@ -261,18 +103,15 @@ The macro is to be defined inside 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. - -The macro was introduced in version 3.9.0. +See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE`](../api/macros/nlohmann_define_type_intrusive.md). ## `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)` This macro is similar to `NLOHMANN_DEFINE_TYPE_INTRUSIVE`. It will not throw an exception in `from_json()` due to a -missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the -type be default constructible. The `from_json()` function default constructs an object and uses its values as the -defaults when calling the `value()` function. +missing value in the JSON object, but can throw due to a mismatched type. The `from_json()` function default constructs +an object and uses its values as the defaults when calling the [`value`](../api/basic_json/value.md) function. -The macro was introduced in version 3.11.0. +See [full documentation of `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT`](../api/macros/nlohmann_define_type_intrusive.md). ## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...)` @@ -283,29 +122,26 @@ The macro is to be defined inside the namespace of the class/struct to create co 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. - -The macro was introduced in version 3.9.0. +See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`](../api/macros/nlohmann_define_type_non_intrusive.md). ## `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...)` This macro is similar to `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`. It will not throw an exception in `from_json()` due to a -missing value in the JSON object, but can throw due to a mismatched type. In order to support that it requires that the -type be default constructible. The `from_json()` function default constructs an object and uses its values as the -defaults when calling the `value()` function. +missing value in the JSON object, but can throw due to a mismatched type. The `from_json()` function default constructs +an object and uses its values as the defaults when calling the [`value`](../api/basic_json/value.md) function. -The macro was introduced in version 3.11.0. +See [full documentation of `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`](../api/macros/nlohmann_define_type_non_intrusive.md). ## `NLOHMANN_JSON_SERIALIZE_ENUM(type, ...)` This macro simplifies the serialization/deserialization of enum types. See [Specializing enum conversion](enum_conversion.md) for more information. -The macro was introduced in version 3.4.0. +See [full documentation of `NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md). ## `NLOHMANN_JSON_VERSION_MAJOR`, `NLOHMANN_JSON_VERSION_MINOR`, `NLOHMANN_JSON_VERSION_PATCH` These macros are defined by the library and contain the version numbers according to -[Semantic Versioning 2.0.0](https://semver.org). +[Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). -The macros were introduced in version 3.1.0. +See [full documentation of `NLOHMANN_JSON_VERSION_MAJOR`, `NLOHMANN_JSON_VERSION_MINOR`, and `NLOHMANN_JSON_VERSION_PATCH`](../api/macros/nlohmann_json_version_major.md). diff --git a/doc/mkdocs/docs/features/types/number_handling.md b/doc/mkdocs/docs/features/types/number_handling.md index dd6507353..03d8c9c69 100644 --- a/doc/mkdocs/docs/features/types/number_handling.md +++ b/doc/mkdocs/docs/features/types/number_handling.md @@ -275,7 +275,7 @@ The rationale is twofold: ### Determine number types -As the example in [Number conversion](#number_conversion) shows, there are different functions to determine the type of +As the example in [Number conversion](#number-conversion) shows, there are different functions to determine the type of the stored number: - [`is_number()`](../../api/basic_json/is_number.md) returns `#!c true` for any number type diff --git a/doc/mkdocs/docs/home/exceptions.md b/doc/mkdocs/docs/home/exceptions.md index 1241926cf..666d225bd 100644 --- a/doc/mkdocs/docs/home/exceptions.md +++ b/doc/mkdocs/docs/home/exceptions.md @@ -28,9 +28,9 @@ class json::parse_error { ### Switch off exceptions -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 `#!cpp throw`), `JSON_TRY_USER` (overriding `#!cpp try`), and `JSON_CATCH_USER` (overriding `#!cpp catch`). +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`](../api/macros/json_noexception.md). In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `#!cpp throw`), `JSON_TRY_USER` (overriding `#!cpp try`), and `JSON_CATCH_USER` (overriding `#!cpp 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. +Note that [`JSON_THROW_USER`](../api/macros/json_throw_user.md) should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. ??? example @@ -52,6 +52,8 @@ Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or Note the explanatory [`what()`](https://en.cppreference.com/w/cpp/error/exception/what) string of exceptions is not available for MSVC if exceptions are disabled, see [#2824](https://github.com/nlohmann/json/discussions/2824). +See [documentation of `JSON_TRY_USER`, `JSON_CATCH_USER` and `JSON_THROW_USER`](../api/macros/json_throw_user.md) for more information. + ### Extended diagnostic messages Exceptions in the library are thrown in the local context of the JSON value they are detected. This makes detailed diagnostics messages, and hence debugging, difficult. @@ -88,6 +90,7 @@ As this global context comes at the price of storing one additional pointer per Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type. +See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for more information. ## Parse errors diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index ddd55e5c9..59f0ae700 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -64,6 +64,7 @@ nav: - features/parsing/parse_exceptions.md - features/parsing/parser_callbacks.md - features/parsing/sax_interface.md + - features/assertions.md - features/enum_conversion.md - features/macros.md - Types: @@ -232,7 +233,30 @@ nav: - 'ordered_map': api/ordered_map.md - macros: - 'Overview': api/macros/index.md - - 'JSON_ASSERT(x)': api/macros/json_assert.md + - 'JSON_ASSERT': api/macros/json_assert.md + - 'JSON_CATCH_USER': api/macros/json_throw_user.md + - 'JSON_DIAGNOSTICS': api/macros/json_diagnostics.md + - 'JSON_HAS_CPP_11': api/macros/json_has_cpp_11.md + - 'JSON_HAS_CPP_14': api/macros/json_has_cpp_11.md + - 'JSON_HAS_CPP_17': api/macros/json_has_cpp_11.md + - 'JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md + - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM': api/macros/json_has_filesystem.md + - 'JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md + - 'JSON_NOEXCEPTION': api/macros/json_noexception.md + - 'JSON_NO_IO': api/macros/json_no_io.md + - 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md + - 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md + - 'JSON_THROW_USER': api/macros/json_throw_user.md + - 'JSON_TRY_USER': api/macros/json_throw_user.md + - 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE': api/macros/nlohmann_define_type_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_type_non_intrusive.md + - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_non_intrusive.md + - 'NLOHMANN_JSON_SERIALIZE_ENUM': api/macros/nlohmann_json_serialize_enum.md + - 'NLOHMANN_JSON_VERSION_MAJOR': api/macros/nlohmann_json_version_major.md + - 'NLOHMANN_JSON_VERSION_MINOR': api/macros/nlohmann_json_version_major.md + - 'NLOHMANN_JSON_VERSION_PATCH': api/macros/nlohmann_json_version_major.md # Extras extra: @@ -289,6 +313,7 @@ plugins: lang: en - minify: minify_html: true + - git-revision-date-localized extra_javascript: - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML diff --git a/doc/mkdocs/requirements.txt b/doc/mkdocs/requirements.txt index b64e9b873..aa9fcaf0e 100644 --- a/doc/mkdocs/requirements.txt +++ b/doc/mkdocs/requirements.txt @@ -1,29 +1,48 @@ -click>=7.1.2 -future>=0.18.2 -htmlmin>=0.1.12 -httplib2>=0.18.1 -importlib-metadata>=1.6.0 -Jinja2>=2.11.2 -joblib>=0.15.1 -jsmin>=2.2.2 -livereload>=2.6.1 -lunr>=0.5.8 -Markdown>=3.2.2 -markdown-include>=0.5.1 -MarkupSafe>=1.1.1 -mkdocs>=1.1.2 -mkdocs-material>=5.2.1 -mkdocs-material-extensions>=1.0 -mkdocs-minify-plugin>=0.3.0 -mkdocs-simple-hooks>=0.1.1 -nltk>=3.5 -plantuml>=0.3.0 -plantuml-markdown>=3.2.2 -Pygments>=2.6.1 -pymdown-extensions>=7.1 -PyYAML>=5.3.1 -regex>=2020.5.14 -six>=1.15.0 -tornado>=6.0.4 -tqdm>=4.46.0 -zipp>=3.1.0 +Babel==2.10.1 +certifi==2021.10.8 +charset-normalizer==2.0.12 +click==8.1.2 +csscompressor==0.9.5 +future==0.18.2 +ghp-import==2.0.2 +gitdb==4.0.9 +GitPython==3.1.27 +htmlmin==0.1.12 +httplib2==0.20.4 +idna==3.3 +importlib-metadata==4.11.3 +Jinja2==3.1.1 +joblib==1.1.0 +jsmin==3.0.1 +livereload==2.6.3 +lunr==0.6.2 +Markdown==3.3.6 +markdown-include==0.6.0 +MarkupSafe==2.1.1 +mergedeep==1.3.4 +mkdocs==1.3.0 +mkdocs-git-revision-date-localized-plugin==1.0.1 +mkdocs-material==8.2.10 +mkdocs-material-extensions==1.0.3 +mkdocs-minify-plugin==0.5.0 +mkdocs-simple-hooks==0.1.5 +nltk==3.7 +packaging==21.3 +plantuml==0.3.0 +plantuml-markdown==3.5.2 +Pygments==2.11.0 +pymdown-extensions==9.3 +pyparsing==3.0.8 +python-dateutil==2.8.2 +pytz==2022.1 +PyYAML==6.0 +pyyaml_env_tag==0.1 +regex==2022.4.24 +requests==2.27.1 +six==1.16.0 +smmap==5.0.0 +tornado==6.1 +tqdm==4.64.0 +urllib3==1.26.9 +watchdog==2.1.7 +zipp==3.8.0 diff --git a/doc/mkdocs/scripts/check_structure.py b/doc/mkdocs/scripts/check_structure.py old mode 100644 new mode 100755 index cacc51cb4..92f6ff182 --- a/doc/mkdocs/scripts/check_structure.py +++ b/doc/mkdocs/scripts/check_structure.py @@ -2,10 +2,19 @@ import glob import os.path +import re + +warnings = 0 + + +def report(rule, location, description): + global warnings + warnings += 1 + print(f'{warnings:3}. {location}: {description} [{rule}]') def check_structure(): - expected_headers = [ + expected_sections = [ 'Template parameters', 'Specializations', 'Iterator invalidation', @@ -23,13 +32,14 @@ def check_structure(): 'Exceptions', 'Complexity', 'Possible implementation', + 'Default definition', 'Notes', 'Examples', 'See also', 'Version history' ] - required_headers = [ + required_sections = [ 'Examples', 'Version history' ] @@ -37,8 +47,8 @@ def check_structure(): files = sorted(glob.glob('api/**/*.md', recursive=True)) for file in files: with open(file) as file_content: - header_idx = -1 - existing_headers = [] + section_idx = -1 + existing_sections = [] in_initial_code_example = False previous_line = None h1sections = 0 @@ -51,50 +61,55 @@ def check_structure(): # there should only be one top-level title if h1sections > 1: - print(f'{file}:{lineno+1}: Error: unexpected top-level title "{line}"!') + report('structure/unexpected_section', f'{file}:{lineno+1}', f'unexpected top-level title "{line}"') h1sections = 1 # Overview pages should have a better title if line == '# Overview': - print(f'{file}:{lineno+1}: Error: overview pages should have a better title!') + report('style/title', f'{file}:{lineno+1}', 'overview pages should have a better title than "Overview"') # lines longer than 160 characters are bad (unless they are tables) if len(line) > 160 and '|' not in line: - print(f'{file}:{lineno+1}: Error: line is too long ({len(line)} vs. 160 chars)!') + report('whitespace/line_length', f'{file}:{lineno+1}', f'line is too long ({len(line)} vs. 160 chars)') - # check if headers are correct + # check if sections are correct if line.startswith('## '): - header = line.strip('## ') - existing_headers.append(header) + current_section = line.strip('## ') + existing_sections.append(current_section) - if header in expected_headers: - idx = expected_headers.index(header) - if idx <= header_idx: - print(f'{file}:{lineno+1}: Error: header "{header}" is in an unexpected order (should be before "{expected_headers[header_idx]}")!') - header_idx = idx + if current_section in expected_sections: + idx = expected_sections.index(current_section) + if idx <= section_idx: + report('structure/section_order', f'{file}:{lineno+1}', f'section "{current_section}" is in an unexpected order (should be before "{expected_sections[section_idx]}")') + section_idx = idx else: - print(f'{file}:{lineno+1}: Error: header "{header}" is not part of the expected headers!') + report('structure/unknown_section', f'{file}:{lineno+1}', f'section "{current_section}" is not part of the expected sections') # code example - if line == '```cpp' and header_idx == -1: + if line == '```cpp' and section_idx == -1: in_initial_code_example = True if in_initial_code_example and line.startswith('//'): if any(map(str.isdigit, line)) and '(' not in line: - print(f'{file}:{lineno+1}: Number should be in parentheses: {line}') + report('style/numbering', f'{file}:{lineno+1}', 'number should be in parentheses: {line}') if line == '```' and in_initial_code_example: in_initial_code_example = False # consecutive blank lines are bad if line == '' and previous_line == '': - print(f'{file}:{lineno}-{lineno+1}: Error: Consecutive blank lines!') + report('whitespace/blank_lines', f'{file}:{lineno}-{lineno+1}', 'consecutive blank lines') + + # check that non-example admonitions have titles + untitled_admonition = re.match(r'^(\?\?\?|!!!) ([^ ]+)$', line) + if untitled_admonition and untitled_admonition.group(2) != 'example': + report('style/admonition_title', f'{file}:{lineno}', f'"{untitled_admonition.group(2)}" admonitions should have a title') previous_line = line - for required_header in required_headers: - if required_header not in existing_headers: - print(f'{file}:{lineno+1}: Error: required header "{required_header}" was not found!') + for required_section in required_sections: + if required_section not in existing_sections: + report('structure/missing_section', f'{file}:{lineno+1}', f'required section "{required_section}" was not found') def check_examples(): @@ -113,9 +128,11 @@ def check_examples(): break if not found: - print(f'{example_file}: Error: example file is not used in any documentation file!') + report('examples/missing', f'{example_file}', 'example file is not used in any documentation file') if __name__ == '__main__': + print(120 * '-') check_structure() check_examples() + print(120 * '-') From ee516614813c4ef5762d436f26f2138f09c96410 Mon Sep 17 00:00:00 2001 From: Qianqian Fang Date: Fri, 29 Apr 2022 15:17:30 -0400 Subject: [PATCH 048/110] Support UBJSON-derived Binary JData (BJData) format (#3336) * support UBJSON-derived Binary JData (BJData) format * fix Codacy warning * partially fix VS compilation errors * fix additional VS errors * fix more VS compilation errors * fix additional warnings and errors for clang and msvc * add more tests to cover the new bjdata types * add tests for optimized ndarray, improve coverage, fix clang/gcc warnings * gcc warn useless conversion but msvc gives an error * fix ci_test errors * complete test coverage, fix ci_test errors * add half precision error test * fix No newline at end of file error by clang * simplify endian condition, format unit-bjdata * remove broken test due to alloc limit * full coverage, I hope * move bjdata new markers from default to the same level as ubjson markers * fix ci errors, add tests for new bjdata switch structure * make is_bjdata const after using initializer list * remove the unwanted assert * move is_bjdata to an optional param to write_ubjson * pass use_bjdata via output adapter * revert order to avoid msvc 2015 unreferenced formal param error * update BJData Spect V1 Draft-2 URL after spec release * amalgamate code * code polishing following @gregmarr's feedback * make use_bjdata a non-default parameter * fix ci error, remove unwanted param comment * encode and decode bjdata ndarray in jdata annotations, enable roundtrip tests * partially fix ci errors, add tests to improve coverage * polish patch to remove ci errors * fix a ndarray dim vector condition * fix clang tidy error * add sax test cases for ndarray * add additional sax event tests * adjust sax event numbering * fix sax tests * ndarray can only be used with array containers, discard if used in object * complete test coverage * disable [{SHTFNZ in optimized type due to security risks in #2793 and hampered readability * fix ci error * move OutputIsLittleEndian from tparam to param to replace use_bjdata * fix ci clang gcc error * fix ci static analysis error * update json_test_data to 3.1.0, enable file-based bjdata unit tests * fix stack overflow error on msvc 2019 and 2022 * use https link, update sax_parse_error after rebase * make input_format const and use initializer * return bool for write_bjdata_ndarray * test write_bjdata_ndarray return value as boolean * fix ci error --- README.md | 6 +- cmake/ci.cmake | 2 +- cmake/download_test_data.cmake | 2 +- .../nlohmann/detail/input/binary_reader.hpp | 420 ++- .../nlohmann/detail/input/input_adapters.hpp | 2 +- .../nlohmann/detail/output/binary_writer.hpp | 291 +- include/nlohmann/json.hpp | 111 +- single_include/nlohmann/json.hpp | 824 +++- test/CMakeLists.txt | 2 +- test/Makefile | 5 +- test/src/fuzzer-parse_bjdata.cpp | 84 + test/src/unit-bjdata.cpp | 3355 +++++++++++++++++ 12 files changed, 4877 insertions(+), 227 deletions(-) create mode 100644 test/src/fuzzer-parse_bjdata.cpp create mode 100644 test/src/unit-bjdata.cpp diff --git a/README.md b/README.md index 20e7c48ea..c3990a69b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ - [Implicit conversions](#implicit-conversions) - [Conversions to/from arbitrary types](#arbitrary-types-conversions) - [Specializing enum conversion](#specializing-enum-conversion) - - [Binary formats (BSON, CBOR, MessagePack, and UBJSON)](#binary-formats-bson-cbor-messagepack-and-ubjson) + - [Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData)](#binary-formats-bson-cbor-messagepack-ubjson-and-bjdata) - [Supported compilers](#supported-compilers) - [Integration](#integration) - [CMake](#cmake) @@ -961,9 +961,9 @@ Other Important points: - When using `get()`, undefined JSON values will default to the first pair specified in your map. Select this default pair carefully. - If an enum or JSON value is specified more than once in your map, the first matching occurrence from the top of the map will be returned when converting to or from JSON. -### Binary formats (BSON, CBOR, MessagePack, and UBJSON) +### Binary formats (BSON, CBOR, MessagePack, UBJSON, and BJData) -Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](https://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), and [UBJSON](https://ubjson.org) (Universal Binary JSON Specification) to efficiently encode JSON values to byte vectors and to decode such vectors. +Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [BSON](https://bsonspec.org) (Binary JSON), [CBOR](https://cbor.io) (Concise Binary Object Representation), [MessagePack](https://msgpack.org), [UBJSON](https://ubjson.org) (Universal Binary JSON Specification) and [BJData](https://neurojson.org/bjdata) (Binary JData) to efficiently encode JSON values to byte vectors and to decode such vectors. ```cpp // create a JSON value diff --git a/cmake/ci.cmake b/cmake/ci.cmake index be564fa99..5ae05a9d9 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -683,7 +683,7 @@ add_custom_target(ci_infer add_custom_target(ci_offline_testdata COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data - COMMAND cd ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data && ${GIT_TOOL} clone -c advice.detachedHead=false --branch v3.0.0 https://github.com/nlohmann/json_test_data.git --quiet --depth 1 + COMMAND cd ${PROJECT_BINARY_DIR}/build_offline_testdata/test_data && ${GIT_TOOL} clone -c advice.detachedHead=false --branch v3.1.0 https://github.com/nlohmann/json_test_data.git --quiet --depth 1 COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_TestDataDirectory=${PROJECT_BINARY_DIR}/build_offline_testdata/test_data/json_test_data diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index f516a7c3b..1bb998dae 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,5 +1,5 @@ set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) -set(JSON_TEST_DATA_VERSION 3.0.0) +set(JSON_TEST_DATA_VERSION 3.1.0) # if variable is set, use test data from given directory rather than downloading them if(JSON_TestDataDirectory) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 4041cedba..6474b8b05 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -12,6 +12,7 @@ #include // char_traits, string #include // make_pair, move #include // vector +#include // map #include #include @@ -74,7 +75,7 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format) { (void)detail::is_sax_static_asserts {}; } @@ -118,6 +119,7 @@ class binary_reader break; case input_format_t::ubjson: + case input_format_t::bjdata: result = parse_ubjson_internal(); break; @@ -129,7 +131,7 @@ class binary_reader // strict mode: next byte must be EOF if (result && strict) { - if (format == input_format_t::ubjson) + if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata) { get_ignore_noop(); } @@ -141,7 +143,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, - exception_message(format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); + exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); } } @@ -1844,7 +1846,7 @@ class binary_reader get(); // TODO(niels): may we ignore N here? } - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) { return false; } @@ -1854,52 +1856,154 @@ class binary_reader case 'U': { std::uint8_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'i': { std::int8_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'I': { std::int16_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'l': { std::int32_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'L': { std::int64_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); } default: - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr)); + break; } + auto last_token = get_token_string(); + std::string message; + + if (input_format != input_format_t::bjdata) + { + message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token; + } + else + { + message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token; + } + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr)); + } + + /*! + @param[out] dim an integer vector storing the ND array dimensions + @return whether reading ND array size vector is successful + */ + bool get_ubjson_ndarray_size(std::vector& dim) + { + std::pair size_and_type; + size_t dimlen = 0; + + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, size_and_type.second))) + { + return false; + } + dim.push_back(dimlen); + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen))) + { + return false; + } + dim.push_back(dimlen); + } + } + } + else + { + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, current))) + { + return false; + } + dim.push_back(dimlen); + get_ignore_noop(); + } + } + return true; } /*! @param[out] result determined size @return whether size determination completed */ - bool get_ubjson_size_value(std::size_t& result) + bool get_ubjson_size_value(std::size_t& result, char_int_type prefix = 0) { - switch (get_ignore_noop()) + if (prefix == 0) + { + prefix = get_ignore_noop(); + } + + switch (prefix) { case 'U': { std::uint8_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -1910,7 +2014,7 @@ class binary_reader case 'i': { std::int8_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -1921,7 +2025,7 @@ class binary_reader case 'I': { std::int16_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -1932,7 +2036,7 @@ class binary_reader case 'l': { std::int32_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -1943,7 +2047,7 @@ class binary_reader case 'L': { std::int64_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -1951,13 +2055,105 @@ class binary_reader return true; } - default: + case 'u': { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr)); + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = static_cast(number); + return true; } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = detail::conditional_static_cast(number); + return true; + } + + case '[': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::vector dim; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) + { + return false; + } + if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector + { + result = dim.at(dim.size() - 1); + return true; + } + if (!dim.empty()) // if ndarray, convert to an object in JData annotated array format + { + string_t key = "_ArraySize_"; + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size()))) + { + return false; + } + result = 1; + for (auto i : dim) + { + result *= i; + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(static_cast(i)))) + { + return false; + } + } + result |= (1ull << (sizeof(result) * 8 - 1)); // low 63 bit of result stores the total element count, sign-bit indicates ndarray + return sax->end_array(); + } + result = 0; + return true; + } + + default: + break; } + auto last_token = get_token_string(); + std::string message; + + if (input_format != input_format_t::bjdata) + { + message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token; + } + else + { + message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token; + } + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr)); } /*! @@ -1979,8 +2175,10 @@ class binary_reader if (current == '$') { + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + result.second = get(); // must not ignore 'N', because 'N' maybe the type - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type") || (input_format == input_format_t::bjdata && std::find(bjdx.begin(), bjdx.end(), result.second) != bjdx.end() ))) { return false; } @@ -1988,13 +2186,13 @@ class binary_reader get_ignore_noop(); if (JSON_HEDLEY_UNLIKELY(current != '#')) { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) { return false; } auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); + exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); } return get_ubjson_size_value(result.first); @@ -2017,7 +2215,7 @@ class binary_reader switch (prefix) { case std::char_traits::eof(): // EOF - return unexpect_eof(input_format_t::ubjson, "value"); + return unexpect_eof(input_format, "value"); case 'T': // true return sax->boolean(true); @@ -2030,43 +2228,125 @@ class binary_reader case 'U': { std::uint8_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + return get_number(input_format, number) && sax->number_unsigned(number); } case 'i': { std::int8_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); } case 'I': { std::int16_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); } case 'l': { std::int32_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); } case 'L': { std::int64_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'h': + { + if (input_format != input_format_t::bjdata) + { + break; + } + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte2 << 8u) + byte1); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); } case 'd': { float number{}; - return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + return get_number(input_format, number) && sax->number_float(static_cast(number), ""); } case 'D': { double number{}; - return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + return get_number(input_format, number) && sax->number_float(static_cast(number), ""); } case 'H': @@ -2077,7 +2357,7 @@ class binary_reader case 'C': // char { get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char"))) { return false; } @@ -2085,7 +2365,7 @@ class binary_reader { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); + exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); } string_t s(1, static_cast(current)); return sax->string(s); @@ -2104,12 +2384,10 @@ class binary_reader return get_ubjson_object(); default: // anything else - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr)); - } + break; } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr)); } /*! @@ -2123,6 +2401,44 @@ class binary_reader return false; } + // detect and encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata): + // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]} + + if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1))) + { + std::map bjdtype = {{'U', "uint8"}, {'i', "int8"}, {'u', "uint16"}, {'I', "int16"}, + {'m', "uint32"}, {'l', "int32"}, {'M', "uint64"}, {'L', "int64"}, {'d', "single"}, {'D', "double"}, {'C', "char"} + }; + + string_t key = "_ArrayType_"; + if (JSON_HEDLEY_UNLIKELY(bjdtype.count(size_and_type.second) == 0 || !sax->key(key) || !sax->string(bjdtype[size_and_type.second]) )) + { + return false; + } + + if (size_and_type.second == 'C') + { + size_and_type.second = 'U'; + } + + size_and_type.first &= ~(1ull << (sizeof(std::size_t) * 8 - 1)); + key = "_ArrayData_"; + if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) )) + { + return false; + } + + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + + return (sax->end_array() && sax->end_object()); + } + if (size_and_type.first != string_t::npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) @@ -2185,6 +2501,11 @@ class binary_reader return false; } + if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1))) + { + return false; + } + string_t key; if (size_and_type.first != string_t::npos) { @@ -2267,7 +2588,7 @@ class binary_reader for (std::size_t i = 0; i < size; ++i) { get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) { return false; } @@ -2286,7 +2607,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, - exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); + exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } switch (result_number) @@ -2313,7 +2634,7 @@ class binary_reader case token_type::literal_or_value: default: return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, - exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); + exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } } @@ -2362,6 +2683,8 @@ class binary_reader @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. + On the other hand, BSON and BJData use little endian and should reorder + on big endian systems. */ template bool get_number(const input_format_t format, NumberType& result) @@ -2377,7 +2700,7 @@ class binary_reader } // reverse byte order prior to conversion if necessary - if (is_little_endian != InputIsLittleEndian) + if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) { vec[sizeof(NumberType) - i - 1] = static_cast(current); } @@ -2514,6 +2837,10 @@ class binary_reader error_msg += "BSON"; break; + case input_format_t::bjdata: + error_msg += "BJData"; + break; + case input_format_t::json: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE @@ -2535,6 +2862,9 @@ class binary_reader /// whether we can assume little endianness const bool is_little_endian = little_endianness(); + /// input format + const input_format_t input_format = input_format_t::json; + /// the SAX parser json_sax_t* sax = nullptr; }; diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index d196aec54..4c1ad3cef 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -23,7 +23,7 @@ namespace nlohmann namespace detail { /// the supported input formats -enum class input_format_t { json, cbor, msgpack, ubjson, bson }; +enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata }; //////////////////// // input adapters // diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index c018b8ccd..bb44a1811 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -2,12 +2,14 @@ #include // reverse #include // array +#include // map #include // isnan, isinf #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcpy #include // numeric_limits #include // string #include // move +#include // vector #include #include @@ -724,9 +726,11 @@ class binary_writer @param[in] use_count whether to use '#' prefixes (optimized format) @param[in] use_type whether to use '$' prefixes (optimized format) @param[in] add_prefix whether prefixes need to be used for this value + @param[in] use_bjdata whether write in BJData format, default is false */ void write_ubjson(const BasicJsonType& j, const bool use_count, - const bool use_type, const bool add_prefix = true) + const bool use_type, const bool add_prefix = true, + const bool use_bjdata = false) { switch (j.type()) { @@ -752,19 +756,19 @@ class binary_writer case value_t::number_integer: { - write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata); break; } case value_t::number_unsigned: { - write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata); break; } case value_t::number_float: { - write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata); break; } @@ -774,7 +778,7 @@ class binary_writer { oa->write_character(to_char_type('S')); } - write_number_with_ubjson_prefix(j.m_value.string->size(), true); + write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata); oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); @@ -792,14 +796,16 @@ class binary_writer if (use_type && !j.m_value.array->empty()) { JSON_ASSERT(use_count); - const CharType first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), - [this, first_prefix](const BasicJsonType & v) + [this, first_prefix, use_bjdata](const BasicJsonType & v) { - return ubjson_prefix(v) == first_prefix; + return ubjson_prefix(v, use_bjdata) == first_prefix; }); - if (same_prefix) + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + + if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) { prefix_required = false; oa->write_character(to_char_type('$')); @@ -810,12 +816,12 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_value.array->size(), true); + write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata); } for (const auto& el : *j.m_value.array) { - write_ubjson(el, use_count, use_type, prefix_required); + write_ubjson(el, use_count, use_type, prefix_required, use_bjdata); } if (!use_count) @@ -843,7 +849,7 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata); } if (use_type) @@ -871,6 +877,14 @@ class binary_writer case value_t::object: { + if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end()) + { + if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + { + break; + } + } + if (add_prefix) { oa->write_character(to_char_type('{')); @@ -880,14 +894,16 @@ class binary_writer if (use_type && !j.m_value.object->empty()) { JSON_ASSERT(use_count); - const CharType first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const bool same_prefix = std::all_of(j.begin(), j.end(), - [this, first_prefix](const BasicJsonType & v) + [this, first_prefix, use_bjdata](const BasicJsonType & v) { - return ubjson_prefix(v) == first_prefix; + return ubjson_prefix(v, use_bjdata) == first_prefix; }); - if (same_prefix) + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + + if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) { prefix_required = false; oa->write_character(to_char_type('$')); @@ -898,16 +914,16 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_value.object->size(), true); + write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata); } for (const auto& el : *j.m_value.object) { - write_number_with_ubjson_prefix(el.first.size(), true); + write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata); oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); - write_ubjson(el.second, use_count, use_type, prefix_required); + write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata); } if (!use_count) @@ -974,7 +990,7 @@ class binary_writer const double value) { write_bson_entry_header(name, 0x01); - write_number(value); + write_number(value, true); } /*! @@ -993,7 +1009,7 @@ class binary_writer { write_bson_entry_header(name, 0x02); - write_number(static_cast(value.size() + 1ul)); + write_number(static_cast(value.size() + 1ul), true); oa->write_characters( reinterpret_cast(value.c_str()), value.size() + 1); @@ -1026,12 +1042,12 @@ class binary_writer if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) { write_bson_entry_header(name, 0x10); // int32 - write_number(static_cast(value)); + write_number(static_cast(value), true); } else { write_bson_entry_header(name, 0x12); // int64 - write_number(static_cast(value)); + write_number(static_cast(value), true); } } @@ -1054,12 +1070,12 @@ class binary_writer if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); - write_number(static_cast(j.m_value.number_unsigned)); + write_number(static_cast(j.m_value.number_unsigned), true); } else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); - write_number(static_cast(j.m_value.number_unsigned)); + write_number(static_cast(j.m_value.number_unsigned), true); } else { @@ -1107,7 +1123,7 @@ class binary_writer const typename BasicJsonType::array_t& value) { write_bson_entry_header(name, 0x04); // array - write_number(static_cast(calc_bson_array_size(value))); + write_number(static_cast(calc_bson_array_size(value)), true); std::size_t array_index = 0ul; @@ -1127,7 +1143,7 @@ class binary_writer { write_bson_entry_header(name, 0x05); - write_number(static_cast(value.size())); + write_number(static_cast(value.size()), true); write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); oa->write_characters(reinterpret_cast(value.data()), value.size()); @@ -1249,7 +1265,7 @@ class binary_writer */ void write_bson_object(const typename BasicJsonType::object_t& value) { - write_number(static_cast(calc_bson_object_size(value))); + write_number(static_cast(calc_bson_object_size(value)), true); for (const auto& el : value) { @@ -1295,20 +1311,22 @@ class binary_writer template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, - const bool add_prefix) + const bool add_prefix, + const bool use_bjdata) { if (add_prefix) { oa->write_character(get_ubjson_float_prefix(n)); } - write_number(n); + write_number(n, use_bjdata); } // UBJSON: write number (unsigned integer) template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, - const bool add_prefix) + const bool add_prefix, + const bool use_bjdata) { if (n <= static_cast((std::numeric_limits::max)())) { @@ -1316,7 +1334,7 @@ class binary_writer { oa->write_character(to_char_type('i')); // int8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if (n <= (std::numeric_limits::max)()) { @@ -1324,7 +1342,7 @@ class binary_writer { oa->write_character(to_char_type('U')); // uint8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if (n <= static_cast((std::numeric_limits::max)())) { @@ -1332,7 +1350,15 @@ class binary_writer { oa->write_character(to_char_type('I')); // int16 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('u')); // uint16 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if (n <= static_cast((std::numeric_limits::max)())) { @@ -1340,7 +1366,15 @@ class binary_writer { oa->write_character(to_char_type('l')); // int32 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('m')); // uint32 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if (n <= static_cast((std::numeric_limits::max)())) { @@ -1348,7 +1382,15 @@ class binary_writer { oa->write_character(to_char_type('L')); // int64 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('M')); // uint64 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else { @@ -1358,7 +1400,7 @@ class binary_writer } const auto number = BasicJsonType(n).dump(); - write_number_with_ubjson_prefix(number.size(), true); + write_number_with_ubjson_prefix(number.size(), true, use_bjdata); for (std::size_t i = 0; i < number.size(); ++i) { oa->write_character(to_char_type(static_cast(number[i]))); @@ -1371,7 +1413,8 @@ class binary_writer std::is_signed::value&& !std::is_floating_point::value, int >::type = 0 > void write_number_with_ubjson_prefix(const NumberType n, - const bool add_prefix) + const bool add_prefix, + const bool use_bjdata) { if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -1379,7 +1422,7 @@ class binary_writer { oa->write_character(to_char_type('i')); // int8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) { @@ -1387,7 +1430,7 @@ class binary_writer { oa->write_character(to_char_type('U')); // uint8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -1395,7 +1438,15 @@ class binary_writer { oa->write_character(to_char_type('I')); // int16 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)()))) + { + if (add_prefix) + { + oa->write_character(to_char_type('u')); // uint16 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -1403,7 +1454,15 @@ class binary_writer { oa->write_character(to_char_type('l')); // int32 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)()))) + { + if (add_prefix) + { + oa->write_character(to_char_type('m')); // uint32 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -1411,7 +1470,7 @@ class binary_writer { oa->write_character(to_char_type('L')); // int64 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } // LCOV_EXCL_START else @@ -1422,7 +1481,7 @@ class binary_writer } const auto number = BasicJsonType(n).dump(); - write_number_with_ubjson_prefix(number.size(), true); + write_number_with_ubjson_prefix(number.size(), true, use_bjdata); for (std::size_t i = 0; i < number.size(); ++i) { oa->write_character(to_char_type(static_cast(number[i]))); @@ -1434,7 +1493,7 @@ class binary_writer /*! @brief determine the type prefix of container values */ - CharType ubjson_prefix(const BasicJsonType& j) const noexcept + CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept { switch (j.type()) { @@ -1458,10 +1517,18 @@ class binary_writer { return 'I'; } + if (use_bjdata && ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)())) + { + return 'u'; + } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'l'; } + if (use_bjdata && ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)())) + { + return 'm'; + } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'L'; @@ -1484,14 +1551,26 @@ class binary_writer { return 'I'; } + if (use_bjdata && j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'u'; + } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'l'; } + if (use_bjdata && j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'm'; + } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'L'; } + if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'M'; + } // anything else is treated as high-precision number return 'H'; // LCOV_EXCL_LINE } @@ -1525,6 +1604,118 @@ class binary_writer return 'D'; // float 64 } + /*! + @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid + */ + bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type) + { + std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'} + }; + + string_t key = "_ArrayType_"; + auto it = bjdtype.find(static_cast(value.at(key))); + if (it == bjdtype.end()) + { + return true; + } + CharType dtype = it->second; + + key = "_ArraySize_"; + std::size_t len = (value.at(key).empty() ? 0 : 1); + for (const auto& el : value.at(key)) + { + len *= static_cast(el.m_value.number_unsigned); + } + + key = "_ArrayData_"; + if (value.at(key).size() != len) + { + return true; + } + + oa->write_character('['); + oa->write_character('$'); + oa->write_character(dtype); + oa->write_character('#'); + + key = "_ArraySize_"; + write_ubjson(value.at(key), use_count, use_type, true, true); + + key = "_ArrayData_"; + if (dtype == 'U' || dtype == 'C') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'i') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'u') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'I') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'm') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'l') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'M') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'L') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'd') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + else if (dtype == 'D') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + return false; + } + /////////////////////// // Utility functions // /////////////////////// @@ -1532,16 +1723,18 @@ class binary_writer /* @brief write a number to output input @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - @tparam OutputIsLittleEndian Set to true if output data is + @param[in] OutputIsLittleEndian Set to true if output data is required to be little endian + @tparam NumberType the type of the number @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. + On the other hand, BSON and BJData use little endian and should reorder + on big endian systems. */ - template - void write_number(const NumberType n) + template + void write_number(const NumberType n, const bool OutputIsLittleEndian = false) { // step 1: write number to array of length NumberType std::array vec{}; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 1a2da8d45..0a18f1634 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3773,7 +3773,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); } /// @brief generate SAX events @@ -3788,7 +3788,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); } /// @brief generate SAX events @@ -3809,7 +3809,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); } #ifndef JSON_NO_IO /// @brief deserialize from stream @@ -3965,6 +3965,33 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec binary_writer(o).write_ubjson(j, use_size, use_type); } + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static std::vector to_bjdata(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_bjdata(j, result, use_size, use_type); + return result; + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + } + /// @brief create a BSON serialization of a given JSON value /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ static std::vector to_bson(const basic_json& j) @@ -4000,7 +4027,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -4016,7 +4043,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -4043,7 +4070,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -4058,7 +4085,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4073,7 +4100,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4097,7 +4124,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4112,7 +4139,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4127,7 +4154,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4151,10 +4178,64 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bjdata(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ template @@ -4166,7 +4247,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4181,7 +4262,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -4205,7 +4286,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /// @} diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a29c52997..d596180c6 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5402,6 +5402,7 @@ std::size_t hash(const BasicJsonType& j) #include // char_traits, string #include // make_pair, move #include // vector +#include // map // #include @@ -5433,7 +5434,7 @@ namespace nlohmann namespace detail { /// the supported input formats -enum class input_format_t { json, cbor, msgpack, ubjson, bson }; +enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata }; //////////////////// // input adapters // @@ -8450,7 +8451,7 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format) { (void)detail::is_sax_static_asserts {}; } @@ -8494,6 +8495,7 @@ class binary_reader break; case input_format_t::ubjson: + case input_format_t::bjdata: result = parse_ubjson_internal(); break; @@ -8505,7 +8507,7 @@ class binary_reader // strict mode: next byte must be EOF if (result && strict) { - if (format == input_format_t::ubjson) + if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata) { get_ignore_noop(); } @@ -8517,7 +8519,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, - exception_message(format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); + exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); } } @@ -10220,7 +10222,7 @@ class binary_reader get(); // TODO(niels): may we ignore N here? } - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) { return false; } @@ -10230,52 +10232,154 @@ class binary_reader case 'U': { std::uint8_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'i': { std::int8_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'I': { std::int16_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'l': { std::int32_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); } case 'L': { std::int64_t len{}; - return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t len{}; + return get_number(input_format, len) && get_string(input_format, len, result); } default: - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr)); + break; } + auto last_token = get_token_string(); + std::string message; + + if (input_format != input_format_t::bjdata) + { + message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token; + } + else + { + message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token; + } + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr)); + } + + /*! + @param[out] dim an integer vector storing the ND array dimensions + @return whether reading ND array size vector is successful + */ + bool get_ubjson_ndarray_size(std::vector& dim) + { + std::pair size_and_type; + size_t dimlen = 0; + + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, size_and_type.second))) + { + return false; + } + dim.push_back(dimlen); + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen))) + { + return false; + } + dim.push_back(dimlen); + } + } + } + else + { + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, current))) + { + return false; + } + dim.push_back(dimlen); + get_ignore_noop(); + } + } + return true; } /*! @param[out] result determined size @return whether size determination completed */ - bool get_ubjson_size_value(std::size_t& result) + bool get_ubjson_size_value(std::size_t& result, char_int_type prefix = 0) { - switch (get_ignore_noop()) + if (prefix == 0) + { + prefix = get_ignore_noop(); + } + + switch (prefix) { case 'U': { std::uint8_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -10286,7 +10390,7 @@ class binary_reader case 'i': { std::int8_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -10297,7 +10401,7 @@ class binary_reader case 'I': { std::int16_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -10308,7 +10412,7 @@ class binary_reader case 'l': { std::int32_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -10319,7 +10423,7 @@ class binary_reader case 'L': { std::int64_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) { return false; } @@ -10327,13 +10431,105 @@ class binary_reader return true; } - default: + case 'u': { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr)); + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = static_cast(number); + return true; } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) + { + return false; + } + result = detail::conditional_static_cast(number); + return true; + } + + case '[': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::vector dim; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) + { + return false; + } + if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector + { + result = dim.at(dim.size() - 1); + return true; + } + if (!dim.empty()) // if ndarray, convert to an object in JData annotated array format + { + string_t key = "_ArraySize_"; + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size()))) + { + return false; + } + result = 1; + for (auto i : dim) + { + result *= i; + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(static_cast(i)))) + { + return false; + } + } + result |= (1ull << (sizeof(result) * 8 - 1)); // low 63 bit of result stores the total element count, sign-bit indicates ndarray + return sax->end_array(); + } + result = 0; + return true; + } + + default: + break; } + auto last_token = get_token_string(); + std::string message; + + if (input_format != input_format_t::bjdata) + { + message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token; + } + else + { + message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token; + } + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr)); } /*! @@ -10355,8 +10551,10 @@ class binary_reader if (current == '$') { + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + result.second = get(); // must not ignore 'N', because 'N' maybe the type - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type") || (input_format == input_format_t::bjdata && std::find(bjdx.begin(), bjdx.end(), result.second) != bjdx.end() ))) { return false; } @@ -10364,13 +10562,13 @@ class binary_reader get_ignore_noop(); if (JSON_HEDLEY_UNLIKELY(current != '#')) { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) { return false; } auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); + exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); } return get_ubjson_size_value(result.first); @@ -10393,7 +10591,7 @@ class binary_reader switch (prefix) { case std::char_traits::eof(): // EOF - return unexpect_eof(input_format_t::ubjson, "value"); + return unexpect_eof(input_format, "value"); case 'T': // true return sax->boolean(true); @@ -10406,43 +10604,125 @@ class binary_reader case 'U': { std::uint8_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + return get_number(input_format, number) && sax->number_unsigned(number); } case 'i': { std::int8_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); } case 'I': { std::int16_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); } case 'l': { std::int32_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); } case 'L': { std::int64_t number{}; - return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + return get_number(input_format, number) && sax->number_integer(number); + } + + case 'u': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint16_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'm': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint32_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'M': + { + if (input_format != input_format_t::bjdata) + { + break; + } + std::uint64_t number{}; + return get_number(input_format, number) && sax->number_unsigned(number); + } + + case 'h': + { + if (input_format != input_format_t::bjdata) + { + break; + } + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte2 << 8u) + byte1); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); } case 'd': { float number{}; - return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + return get_number(input_format, number) && sax->number_float(static_cast(number), ""); } case 'D': { double number{}; - return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + return get_number(input_format, number) && sax->number_float(static_cast(number), ""); } case 'H': @@ -10453,7 +10733,7 @@ class binary_reader case 'C': // char { get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char"))) { return false; } @@ -10461,7 +10741,7 @@ class binary_reader { auto last_token = get_token_string(); return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); + exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); } string_t s(1, static_cast(current)); return sax->string(s); @@ -10480,12 +10760,10 @@ class binary_reader return get_ubjson_object(); default: // anything else - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr)); - } + break; } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr)); } /*! @@ -10499,6 +10777,44 @@ class binary_reader return false; } + // detect and encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata): + // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]} + + if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1))) + { + std::map bjdtype = {{'U', "uint8"}, {'i', "int8"}, {'u', "uint16"}, {'I', "int16"}, + {'m', "uint32"}, {'l', "int32"}, {'M', "uint64"}, {'L', "int64"}, {'d', "single"}, {'D', "double"}, {'C', "char"} + }; + + string_t key = "_ArrayType_"; + if (JSON_HEDLEY_UNLIKELY(bjdtype.count(size_and_type.second) == 0 || !sax->key(key) || !sax->string(bjdtype[size_and_type.second]) )) + { + return false; + } + + if (size_and_type.second == 'C') + { + size_and_type.second = 'U'; + } + + size_and_type.first &= ~(1ull << (sizeof(std::size_t) * 8 - 1)); + key = "_ArrayData_"; + if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) )) + { + return false; + } + + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + + return (sax->end_array() && sax->end_object()); + } + if (size_and_type.first != string_t::npos) { if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) @@ -10561,6 +10877,11 @@ class binary_reader return false; } + if (input_format == input_format_t::bjdata && size_and_type.first != string_t::npos && size_and_type.first >= (1ull << (sizeof(std::size_t) * 8 - 1))) + { + return false; + } + string_t key; if (size_and_type.first != string_t::npos) { @@ -10643,7 +10964,7 @@ class binary_reader for (std::size_t i = 0; i < size; ++i) { get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) { return false; } @@ -10662,7 +10983,7 @@ class binary_reader if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) { return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, - exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); + exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } switch (result_number) @@ -10689,7 +11010,7 @@ class binary_reader case token_type::literal_or_value: default: return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, - exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); + exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); } } @@ -10738,6 +11059,8 @@ class binary_reader @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. + On the other hand, BSON and BJData use little endian and should reorder + on big endian systems. */ template bool get_number(const input_format_t format, NumberType& result) @@ -10753,7 +11076,7 @@ class binary_reader } // reverse byte order prior to conversion if necessary - if (is_little_endian != InputIsLittleEndian) + if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) { vec[sizeof(NumberType) - i - 1] = static_cast(current); } @@ -10890,6 +11213,10 @@ class binary_reader error_msg += "BSON"; break; + case input_format_t::bjdata: + error_msg += "BJData"; + break; + case input_format_t::json: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE @@ -10911,6 +11238,9 @@ class binary_reader /// whether we can assume little endianness const bool is_little_endian = little_endianness(); + /// input format + const input_format_t input_format = input_format_t::json; + /// the SAX parser json_sax_t* sax = nullptr; }; @@ -13427,12 +13757,14 @@ class json_ref #include // reverse #include // array +#include // map #include // isnan, isinf #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcpy #include // numeric_limits #include // string #include // move +#include // vector // #include @@ -14292,9 +14624,11 @@ class binary_writer @param[in] use_count whether to use '#' prefixes (optimized format) @param[in] use_type whether to use '$' prefixes (optimized format) @param[in] add_prefix whether prefixes need to be used for this value + @param[in] use_bjdata whether write in BJData format, default is false */ void write_ubjson(const BasicJsonType& j, const bool use_count, - const bool use_type, const bool add_prefix = true) + const bool use_type, const bool add_prefix = true, + const bool use_bjdata = false) { switch (j.type()) { @@ -14320,19 +14654,19 @@ class binary_writer case value_t::number_integer: { - write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata); break; } case value_t::number_unsigned: { - write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata); break; } case value_t::number_float: { - write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata); break; } @@ -14342,7 +14676,7 @@ class binary_writer { oa->write_character(to_char_type('S')); } - write_number_with_ubjson_prefix(j.m_value.string->size(), true); + write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata); oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); @@ -14360,14 +14694,16 @@ class binary_writer if (use_type && !j.m_value.array->empty()) { JSON_ASSERT(use_count); - const CharType first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), - [this, first_prefix](const BasicJsonType & v) + [this, first_prefix, use_bjdata](const BasicJsonType & v) { - return ubjson_prefix(v) == first_prefix; + return ubjson_prefix(v, use_bjdata) == first_prefix; }); - if (same_prefix) + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + + if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) { prefix_required = false; oa->write_character(to_char_type('$')); @@ -14378,12 +14714,12 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_value.array->size(), true); + write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata); } for (const auto& el : *j.m_value.array) { - write_ubjson(el, use_count, use_type, prefix_required); + write_ubjson(el, use_count, use_type, prefix_required, use_bjdata); } if (!use_count) @@ -14411,7 +14747,7 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata); } if (use_type) @@ -14439,6 +14775,14 @@ class binary_writer case value_t::object: { + if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end()) + { + if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata) + { + break; + } + } + if (add_prefix) { oa->write_character(to_char_type('{')); @@ -14448,14 +14792,16 @@ class binary_writer if (use_type && !j.m_value.object->empty()) { JSON_ASSERT(use_count); - const CharType first_prefix = ubjson_prefix(j.front()); + const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata); const bool same_prefix = std::all_of(j.begin(), j.end(), - [this, first_prefix](const BasicJsonType & v) + [this, first_prefix, use_bjdata](const BasicJsonType & v) { - return ubjson_prefix(v) == first_prefix; + return ubjson_prefix(v, use_bjdata) == first_prefix; }); - if (same_prefix) + std::vector bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type + + if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end())) { prefix_required = false; oa->write_character(to_char_type('$')); @@ -14466,16 +14812,16 @@ class binary_writer if (use_count) { oa->write_character(to_char_type('#')); - write_number_with_ubjson_prefix(j.m_value.object->size(), true); + write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata); } for (const auto& el : *j.m_value.object) { - write_number_with_ubjson_prefix(el.first.size(), true); + write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata); oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); - write_ubjson(el.second, use_count, use_type, prefix_required); + write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata); } if (!use_count) @@ -14542,7 +14888,7 @@ class binary_writer const double value) { write_bson_entry_header(name, 0x01); - write_number(value); + write_number(value, true); } /*! @@ -14561,7 +14907,7 @@ class binary_writer { write_bson_entry_header(name, 0x02); - write_number(static_cast(value.size() + 1ul)); + write_number(static_cast(value.size() + 1ul), true); oa->write_characters( reinterpret_cast(value.c_str()), value.size() + 1); @@ -14594,12 +14940,12 @@ class binary_writer if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) { write_bson_entry_header(name, 0x10); // int32 - write_number(static_cast(value)); + write_number(static_cast(value), true); } else { write_bson_entry_header(name, 0x12); // int64 - write_number(static_cast(value)); + write_number(static_cast(value), true); } } @@ -14622,12 +14968,12 @@ class binary_writer if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x10 /* int32 */); - write_number(static_cast(j.m_value.number_unsigned)); + write_number(static_cast(j.m_value.number_unsigned), true); } else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { write_bson_entry_header(name, 0x12 /* int64 */); - write_number(static_cast(j.m_value.number_unsigned)); + write_number(static_cast(j.m_value.number_unsigned), true); } else { @@ -14675,7 +15021,7 @@ class binary_writer const typename BasicJsonType::array_t& value) { write_bson_entry_header(name, 0x04); // array - write_number(static_cast(calc_bson_array_size(value))); + write_number(static_cast(calc_bson_array_size(value)), true); std::size_t array_index = 0ul; @@ -14695,7 +15041,7 @@ class binary_writer { write_bson_entry_header(name, 0x05); - write_number(static_cast(value.size())); + write_number(static_cast(value.size()), true); write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); oa->write_characters(reinterpret_cast(value.data()), value.size()); @@ -14817,7 +15163,7 @@ class binary_writer */ void write_bson_object(const typename BasicJsonType::object_t& value) { - write_number(static_cast(calc_bson_object_size(value))); + write_number(static_cast(calc_bson_object_size(value)), true); for (const auto& el : value) { @@ -14863,20 +15209,22 @@ class binary_writer template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, - const bool add_prefix) + const bool add_prefix, + const bool use_bjdata) { if (add_prefix) { oa->write_character(get_ubjson_float_prefix(n)); } - write_number(n); + write_number(n, use_bjdata); } // UBJSON: write number (unsigned integer) template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, - const bool add_prefix) + const bool add_prefix, + const bool use_bjdata) { if (n <= static_cast((std::numeric_limits::max)())) { @@ -14884,7 +15232,7 @@ class binary_writer { oa->write_character(to_char_type('i')); // int8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if (n <= (std::numeric_limits::max)()) { @@ -14892,7 +15240,7 @@ class binary_writer { oa->write_character(to_char_type('U')); // uint8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if (n <= static_cast((std::numeric_limits::max)())) { @@ -14900,7 +15248,15 @@ class binary_writer { oa->write_character(to_char_type('I')); // int16 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('u')); // uint16 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if (n <= static_cast((std::numeric_limits::max)())) { @@ -14908,7 +15264,15 @@ class binary_writer { oa->write_character(to_char_type('l')); // int32 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('m')); // uint32 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if (n <= static_cast((std::numeric_limits::max)())) { @@ -14916,7 +15280,15 @@ class binary_writer { oa->write_character(to_char_type('L')); // int64 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('M')); // uint64 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else { @@ -14926,7 +15298,7 @@ class binary_writer } const auto number = BasicJsonType(n).dump(); - write_number_with_ubjson_prefix(number.size(), true); + write_number_with_ubjson_prefix(number.size(), true, use_bjdata); for (std::size_t i = 0; i < number.size(); ++i) { oa->write_character(to_char_type(static_cast(number[i]))); @@ -14939,7 +15311,8 @@ class binary_writer std::is_signed::value&& !std::is_floating_point::value, int >::type = 0 > void write_number_with_ubjson_prefix(const NumberType n, - const bool add_prefix) + const bool add_prefix, + const bool use_bjdata) { if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -14947,7 +15320,7 @@ class binary_writer { oa->write_character(to_char_type('i')); // int8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) { @@ -14955,7 +15328,7 @@ class binary_writer { oa->write_character(to_char_type('U')); // uint8 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -14963,7 +15336,15 @@ class binary_writer { oa->write_character(to_char_type('I')); // int16 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)()))) + { + if (add_prefix) + { + oa->write_character(to_char_type('u')); // uint16 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -14971,7 +15352,15 @@ class binary_writer { oa->write_character(to_char_type('l')); // int32 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); + } + else if (use_bjdata && (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)()))) + { + if (add_prefix) + { + oa->write_character(to_char_type('m')); // uint32 - bjdata only + } + write_number(static_cast(n), use_bjdata); } else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) { @@ -14979,7 +15368,7 @@ class binary_writer { oa->write_character(to_char_type('L')); // int64 } - write_number(static_cast(n)); + write_number(static_cast(n), use_bjdata); } // LCOV_EXCL_START else @@ -14990,7 +15379,7 @@ class binary_writer } const auto number = BasicJsonType(n).dump(); - write_number_with_ubjson_prefix(number.size(), true); + write_number_with_ubjson_prefix(number.size(), true, use_bjdata); for (std::size_t i = 0; i < number.size(); ++i) { oa->write_character(to_char_type(static_cast(number[i]))); @@ -15002,7 +15391,7 @@ class binary_writer /*! @brief determine the type prefix of container values */ - CharType ubjson_prefix(const BasicJsonType& j) const noexcept + CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept { switch (j.type()) { @@ -15026,10 +15415,18 @@ class binary_writer { return 'I'; } + if (use_bjdata && ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)())) + { + return 'u'; + } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'l'; } + if (use_bjdata && ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)())) + { + return 'm'; + } if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'L'; @@ -15052,14 +15449,26 @@ class binary_writer { return 'I'; } + if (use_bjdata && j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'u'; + } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'l'; } + if (use_bjdata && j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'm'; + } if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) { return 'L'; } + if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'M'; + } // anything else is treated as high-precision number return 'H'; // LCOV_EXCL_LINE } @@ -15093,6 +15502,118 @@ class binary_writer return 'D'; // float 64 } + /*! + @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid + */ + bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type) + { + std::map bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'}, + {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'} + }; + + string_t key = "_ArrayType_"; + auto it = bjdtype.find(static_cast(value.at(key))); + if (it == bjdtype.end()) + { + return true; + } + CharType dtype = it->second; + + key = "_ArraySize_"; + std::size_t len = (value.at(key).empty() ? 0 : 1); + for (const auto& el : value.at(key)) + { + len *= static_cast(el.m_value.number_unsigned); + } + + key = "_ArrayData_"; + if (value.at(key).size() != len) + { + return true; + } + + oa->write_character('['); + oa->write_character('$'); + oa->write_character(dtype); + oa->write_character('#'); + + key = "_ArraySize_"; + write_ubjson(value.at(key), use_count, use_type, true, true); + + key = "_ArrayData_"; + if (dtype == 'U' || dtype == 'C') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'i') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'u') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'I') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'm') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'l') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'M') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_unsigned), true); + } + } + else if (dtype == 'L') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_integer), true); + } + } + else if (dtype == 'd') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + else if (dtype == 'D') + { + for (const auto& el : value.at(key)) + { + write_number(static_cast(el.m_value.number_float), true); + } + } + return false; + } + /////////////////////// // Utility functions // /////////////////////// @@ -15100,16 +15621,18 @@ class binary_writer /* @brief write a number to output input @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - @tparam OutputIsLittleEndian Set to true if output data is + @param[in] OutputIsLittleEndian Set to true if output data is required to be little endian + @tparam NumberType the type of the number @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. + On the other hand, BSON and BJData use little endian and should reorder + on big endian systems. */ - template - void write_number(const NumberType n) + template + void write_number(const NumberType n, const bool OutputIsLittleEndian = false) { // step 1: write number to array of length NumberType std::array vec{}; @@ -21221,7 +21744,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); } /// @brief generate SAX events @@ -21236,7 +21759,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) - : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); } /// @brief generate SAX events @@ -21257,7 +21780,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); } #ifndef JSON_NO_IO /// @brief deserialize from stream @@ -21413,6 +21936,33 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec binary_writer(o).write_ubjson(j, use_size, use_type); } + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static std::vector to_bjdata(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_bjdata(j, result, use_size, use_type); + return result; + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + } + /// @brief create a BSON serialization of a given JSON value /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ static std::vector to_bson(const basic_json& j) @@ -21448,7 +21998,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -21464,7 +22014,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -21491,7 +22041,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); return res ? result : basic_json(value_t::discarded); } @@ -21506,7 +22056,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21521,7 +22071,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21545,7 +22095,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21560,7 +22110,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21575,7 +22125,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21599,10 +22149,64 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bjdata(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ template @@ -21614,7 +22218,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::forward(i)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21629,7 +22233,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = detail::input_adapter(std::move(first), std::move(last)); - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -21653,7 +22257,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::json_sax_dom_parser sdp(result, allow_exceptions); auto ia = i.get(); // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) - const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /// @} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2f06def50..1d5c78697 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -72,7 +72,7 @@ endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # avoid stack overflow, see https://github.com/nlohmann/json/issues/2955 - json_test_set_test_options("test-cbor;test-msgpack;test-ubjson" LINK_OPTIONS /STACK:4000000) + json_test_set_test_options("test-cbor;test-msgpack;test-ubjson;test-bjdata" LINK_OPTIONS /STACK:4000000) endif() # disable exceptions for test-disabled_exceptions diff --git a/test/Makefile b/test/Makefile index 2d08a8f7b..3a11ce7dd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,7 +10,7 @@ CXXFLAGS += -std=c++11 CPPFLAGS += -I ../single_include FUZZER_ENGINE = src/fuzzer-driver_afl.cpp -FUZZERS = parse_afl_fuzzer parse_bson_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer parse_ubjson_fuzzer +FUZZERS = parse_afl_fuzzer parse_bson_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer parse_ubjson_fuzzer parse_bjdata_fuzzer fuzzers: $(FUZZERS) parse_afl_fuzzer: @@ -27,3 +27,6 @@ parse_msgpack_fuzzer: parse_ubjson_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_ubjson.cpp -o $@ + +parse_bjdata_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_bjdata.cpp -o $@ diff --git a/test/src/fuzzer-parse_bjdata.cpp b/test/src/fuzzer-parse_bjdata.cpp new file mode 100644 index 000000000..99e799bd3 --- /dev/null +++ b/test/src/fuzzer-parse_bjdata.cpp @@ -0,0 +1,84 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 3.10.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = from_bjdata(data) +- vec = to_bjdata(j1) +- j2 = from_bjdata(vec) +- assert(j1 == j2) +- vec2 = to_bjdata(j1, use_size = true, use_type = false) +- j3 = from_bjdata(vec2) +- assert(j1 == j3) +- vec3 = to_bjdata(j1, use_size = true, use_type = true) +- j4 = from_bjdata(vec3) +- assert(j1 == j4) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +using json = nlohmann::json; + +// see http://llvm.org/docs/LibFuzzer.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + // step 1: parse input + std::vector vec1(data, data + size); + json j1 = json::from_bjdata(vec1); + + try + { + // step 2.1: round trip without adding size annotations to container types + std::vector vec2 = json::to_bjdata(j1, false, false); + + // step 2.2: round trip with adding size annotations but without adding type annonations to container types + std::vector vec3 = json::to_bjdata(j1, true, false); + + // step 2.3: round trip with adding size as well as type annotations to container types + std::vector vec4 = json::to_bjdata(j1, true, true); + + // parse serialization + json j2 = json::from_bjdata(vec2); + json j3 = json::from_bjdata(vec3); + json j4 = json::from_bjdata(vec4); + + // serializations must match + assert(json::to_bjdata(j2, false, false) == vec2); + assert(json::to_bjdata(j3, true, false) == vec3); + assert(json::to_bjdata(j4, true, true) == vec4); + } + catch (const json::parse_error&) + { + // parsing a BJData serialization must not fail + assert(false); + } + } + catch (const json::parse_error&) + { + // parse errors are ok, because input may be random bytes + } + catch (const json::type_error&) + { + // type errors can occur during parsing, too + } + catch (const json::out_of_range&) + { + // out of range errors may happen if provided sizes are excessive + } + + // return 0 - non-zero return values are reserved for future use + return 0; +} diff --git a/test/src/unit-bjdata.cpp b/test/src/unit-bjdata.cpp new file mode 100644 index 000000000..bc6c52833 --- /dev/null +++ b/test/src/unit-bjdata.cpp @@ -0,0 +1,3355 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.10.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2022 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 +#include +#include +#include +#include "test_utils.hpp" + +namespace +{ +class SaxCountdown +{ + public: + explicit SaxCountdown(const int count) : events_left(count) + {} + + bool null() + { + return events_left-- > 0; + } + + bool boolean(bool /*unused*/) + { + return events_left-- > 0; + } + + bool number_integer(json::number_integer_t /*unused*/) + { + return events_left-- > 0; + } + + bool number_unsigned(json::number_unsigned_t /*unused*/) + { + return events_left-- > 0; + } + + bool number_float(json::number_float_t /*unused*/, const std::string& /*unused*/) + { + return events_left-- > 0; + } + + bool string(std::string& /*unused*/) + { + return events_left-- > 0; + } + + bool binary(std::vector& /*unused*/) + { + return events_left-- > 0; + } + + bool start_object(std::size_t /*unused*/) + { + return events_left-- > 0; + } + + bool key(std::string& /*unused*/) + { + return events_left-- > 0; + } + + bool end_object() + { + return events_left-- > 0; + } + + bool start_array(std::size_t /*unused*/) + { + return events_left-- > 0; + } + + bool end_array() + { + return events_left-- > 0; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const json::exception& /*unused*/) // NOLINT(readability-convert-member-functions-to-static) + { + return false; + } + + private: + int events_left = 0; +}; +} // namespace + +TEST_CASE("BJData") +{ + SECTION("individual values") + { + SECTION("discarded") + { + // discarded values are not serialized + json j = json::value_t::discarded; + const auto result = json::to_bjdata(j); + CHECK(result.empty()); + } + + SECTION("null") + { + json j = nullptr; + std::vector expected = {'Z'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("boolean") + { + SECTION("true") + { + json j = true; + std::vector expected = {'T'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("false") + { + json j = false; + std::vector expected = {'F'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("number") + { + SECTION("signed") + { + SECTION("-9223372036854775808..-2147483649 (int64)") + { + std::vector numbers; + numbers.push_back((std::numeric_limits::min)()); + numbers.push_back(-1000000000000000000LL); + numbers.push_back(-100000000000000000LL); + numbers.push_back(-10000000000000000LL); + numbers.push_back(-1000000000000000LL); + numbers.push_back(-100000000000000LL); + numbers.push_back(-10000000000000LL); + numbers.push_back(-1000000000000LL); + numbers.push_back(-100000000000LL); + numbers.push_back(-10000000000LL); + numbers.push_back(-2147483649LL); + for (auto i : numbers) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('L')); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + expected.push_back(static_cast((i >> 32) & 0xff)); + expected.push_back(static_cast((i >> 40) & 0xff)); + expected.push_back(static_cast((i >> 48) & 0xff)); + expected.push_back(static_cast((i >> 56) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 9); + + // check individual bytes + CHECK(result[0] == 'L'); + int64_t restored = (static_cast(result[8]) << 070) + + (static_cast(result[7]) << 060) + + (static_cast(result[6]) << 050) + + (static_cast(result[5]) << 040) + + (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("-2147483648..-32769 (int32)") + { + std::vector numbers; + numbers.push_back(-32769); + numbers.push_back(-100000); + numbers.push_back(-1000000); + numbers.push_back(-10000000); + numbers.push_back(-100000000); + numbers.push_back(-1000000000); + numbers.push_back(-2147483647 - 1); // https://stackoverflow.com/a/29356002/266378 + for (auto i : numbers) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('l')); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 5); + + // check individual bytes + CHECK(result[0] == 'l'); + int32_t restored = (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("-32768..-129 (int16)") + { + for (int32_t i = -32768; i <= -129; ++i) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('I')); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 'I'); + auto restored = static_cast(((result[2] << 8) + result[1])); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("-9263 (int16)") + { + json j = -9263; + std::vector expected = {'I', 0xd1, 0xdb}; + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 'I'); + auto restored = static_cast(((result[2] << 8) + result[1])); + CHECK(restored == -9263); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("-128..-1 (int8)") + { + for (auto i = -128; i <= -1; ++i) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back('i'); + expected.push_back(static_cast(i)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 2); + + // check individual bytes + CHECK(result[0] == 'i'); + CHECK(static_cast(result[1]) == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("0..127 (int8)") + { + for (size_t i = 0; i <= 127; ++i) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('i')); + expected.push_back(static_cast(i)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 2); + + // check individual bytes + CHECK(result[0] == 'i'); + CHECK(result[1] == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("128..255 (uint8)") + { + for (size_t i = 128; i <= 255; ++i) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('U')); + expected.push_back(static_cast(i)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 2); + + // check individual bytes + CHECK(result[0] == 'U'); + CHECK(result[1] == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("256..32767 (int16)") + { + for (size_t i = 256; i <= 32767; ++i) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('I')); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 'I'); + auto restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("32768..65535 (uint16)") + { + for (uint32_t i : + { + 32768u, 55555u, 65535u + }) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('u')); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 'u'); + auto restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("65536..2147483647 (int32)") + { + for (uint32_t i : + { + 65536u, 77777u, 2147483647u + }) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back('l'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 5); + + // check individual bytes + CHECK(result[0] == 'l'); + uint32_t restored = (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("2147483648..4294967295 (uint32)") + { + for (uint32_t i : + { + 2147483648u, 3333333333u, 4294967295u + }) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back('m'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 5); + + // check individual bytes + CHECK(result[0] == 'm'); + uint32_t restored = (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("4294967296..9223372036854775807 (int64)") + { + std::vector v = {4294967296LU, 9223372036854775807LU}; + for (uint64_t i : v) + { + CAPTURE(i) + + // create JSON value with integer number + json j = -1; + j.get_ref() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back('L'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 010) & 0xff)); + expected.push_back(static_cast((i >> 020) & 0xff)); + expected.push_back(static_cast((i >> 030) & 0xff)); + expected.push_back(static_cast((i >> 040) & 0xff)); + expected.push_back(static_cast((i >> 050) & 0xff)); + expected.push_back(static_cast((i >> 060) & 0xff)); + expected.push_back(static_cast((i >> 070) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 9); + + // check individual bytes + CHECK(result[0] == 'L'); + uint64_t restored = (static_cast(result[8]) << 070) + + (static_cast(result[7]) << 060) + + (static_cast(result[6]) << 050) + + (static_cast(result[5]) << 040) + + (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("9223372036854775808..18446744073709551615 (uint64)") + { + std::vector v = {9223372036854775808ull, 18446744073709551615ull}; + for (uint64_t i : v) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('M'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 010) & 0xff)); + expected.push_back(static_cast((i >> 020) & 0xff)); + expected.push_back(static_cast((i >> 030) & 0xff)); + expected.push_back(static_cast((i >> 040) & 0xff)); + expected.push_back(static_cast((i >> 050) & 0xff)); + expected.push_back(static_cast((i >> 060) & 0xff)); + expected.push_back(static_cast((i >> 070) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 9); + + // check individual bytes + CHECK(result[0] == 'M'); + uint64_t restored = (static_cast(result[8]) << 070) + + (static_cast(result[7]) << 060) + + (static_cast(result[6]) << 050) + + (static_cast(result[5]) << 040) + + (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + } + + SECTION("unsigned") + { + SECTION("0..127 (int8)") + { + for (size_t i = 0; i <= 127; ++i) + { + CAPTURE(i) + + // create JSON value with unsigned integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('i'); + expected.push_back(static_cast(i)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 2); + + // check individual bytes + CHECK(result[0] == 'i'); + auto restored = static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("128..255 (uint8)") + { + for (size_t i = 128; i <= 255; ++i) + { + CAPTURE(i) + + // create JSON value with unsigned integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('U'); + expected.push_back(static_cast(i)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 2); + + // check individual bytes + CHECK(result[0] == 'U'); + auto restored = static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("256..32767 (int16)") + { + for (size_t i = 256; i <= 32767; ++i) + { + CAPTURE(i) + + // create JSON value with unsigned integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('I'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 'I'); + auto restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("32768..65535 (uint16)") + { + for (uint32_t i : + { + 32768u, 55555u, 65535u + }) + { + CAPTURE(i) + + // create JSON value with unsigned integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('u'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 'u'); + auto restored = static_cast(static_cast(result[2]) * 256 + static_cast(result[1])); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + SECTION("65536..2147483647 (int32)") + { + for (uint32_t i : + { + 65536u, 77777u, 2147483647u + }) + { + CAPTURE(i) + + // create JSON value with unsigned integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('l'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 5); + + // check individual bytes + CHECK(result[0] == 'l'); + uint32_t restored = (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("2147483648..4294967295 (uint32)") + { + for (uint32_t i : + { + 2147483648u, 3333333333u, 4294967295u + }) + { + CAPTURE(i) + + // create JSON value with unsigned integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('m'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 5); + + // check individual bytes + CHECK(result[0] == 'm'); + uint32_t restored = (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("4294967296..9223372036854775807 (int64)") + { + std::vector v = {4294967296ul, 9223372036854775807ul}; + for (uint64_t i : v) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('L'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 010) & 0xff)); + expected.push_back(static_cast((i >> 020) & 0xff)); + expected.push_back(static_cast((i >> 030) & 0xff)); + expected.push_back(static_cast((i >> 040) & 0xff)); + expected.push_back(static_cast((i >> 050) & 0xff)); + expected.push_back(static_cast((i >> 060) & 0xff)); + expected.push_back(static_cast((i >> 070) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 9); + + // check individual bytes + CHECK(result[0] == 'L'); + uint64_t restored = (static_cast(result[8]) << 070) + + (static_cast(result[7]) << 060) + + (static_cast(result[6]) << 050) + + (static_cast(result[5]) << 040) + + (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("9223372036854775808..18446744073709551615 (uint64)") + { + std::vector v = {9223372036854775808ull, 18446744073709551615ull}; + for (uint64_t i : v) + { + CAPTURE(i) + + // create JSON value with integer number + json j = i; + + // check type + CHECK(j.is_number_unsigned()); + + // create expected byte vector + std::vector expected; + expected.push_back('M'); + expected.push_back(static_cast(i & 0xff)); + expected.push_back(static_cast((i >> 010) & 0xff)); + expected.push_back(static_cast((i >> 020) & 0xff)); + expected.push_back(static_cast((i >> 030) & 0xff)); + expected.push_back(static_cast((i >> 040) & 0xff)); + expected.push_back(static_cast((i >> 050) & 0xff)); + expected.push_back(static_cast((i >> 060) & 0xff)); + expected.push_back(static_cast((i >> 070) & 0xff)); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == 9); + + // check individual bytes + CHECK(result[0] == 'M'); + uint64_t restored = (static_cast(result[8]) << 070) + + (static_cast(result[7]) << 060) + + (static_cast(result[6]) << 050) + + (static_cast(result[5]) << 040) + + (static_cast(result[4]) << 030) + + (static_cast(result[3]) << 020) + + (static_cast(result[2]) << 010) + + static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + } + SECTION("float64") + { + SECTION("3.1415925") + { + double v = 3.1415925; + json j = v; + std::vector expected = + { + 'D', 0xfc, 0xde, 0xa6, 0x3f, 0xfb, 0x21, 0x09, 0x40 + }; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result) == v); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("half-precision float") + { + SECTION("simple half floats") + { + CHECK(json::parse("0.0") == json::from_bjdata(std::vector({'h', 0x00, 0x00}))); + CHECK(json::parse("-0.0") == json::from_bjdata(std::vector({'h', 0x00, 0x80}))); + CHECK(json::parse("1.0") == json::from_bjdata(std::vector({'h', 0x00, 0x3c}))); + CHECK(json::parse("1.5") == json::from_bjdata(std::vector({'h', 0x00, 0x3e}))); + CHECK(json::parse("65504.0") == json::from_bjdata(std::vector({'h', 0xff, 0x7b}))); + } + + SECTION("errors") + { + SECTION("no byte follows") + { + json _; + std::vector vec0 = {'h'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vec0), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vec0), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vec0, true, false).is_discarded()); + } + + SECTION("only one byte follows") + { + json _; + std::vector vec1 = {'h', 0x00}; + CHECK_THROWS_AS(_ = json::from_bjdata(vec1), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vec1), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vec1, true, false).is_discarded()); + } + } + } + + SECTION("half-precision float (edge cases)") + { + SECTION("exp = 0b00000") + { + SECTION("0 (0 00000 0000000000)") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0x00})); + json::number_float_t d{j}; + CHECK(d == 0.0); + } + + SECTION("-0 (1 00000 0000000000)") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0x80})); + json::number_float_t d{j}; + CHECK(d == -0.0); + } + + SECTION("2**-24 (0 00000 0000000001)") + { + json j = json::from_bjdata(std::vector({'h', 0x01, 0x00})); + json::number_float_t d{j}; + CHECK(d == std::pow(2.0, -24.0)); + } + } + + SECTION("exp = 0b11111") + { + SECTION("infinity (0 11111 0000000000)") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0x7c})); + json::number_float_t d{j}; + CHECK(d == std::numeric_limits::infinity()); + CHECK(j.dump() == "null"); + } + + SECTION("-infinity (1 11111 0000000000)") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0xfc})); + json::number_float_t d{j}; + CHECK(d == -std::numeric_limits::infinity()); + CHECK(j.dump() == "null"); + } + } + + SECTION("other values from https://en.wikipedia.org/wiki/Half-precision_floating-point_format") + { + SECTION("1 (0 01111 0000000000)") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0x3c})); + json::number_float_t d{j}; + CHECK(d == 1); + } + + SECTION("-2 (1 10000 0000000000)") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0xc0})); + json::number_float_t d{j}; + CHECK(d == -2); + } + + SECTION("65504 (0 11110 1111111111)") + { + json j = json::from_bjdata(std::vector({'h', 0xff, 0x7b})); + json::number_float_t d{j}; + CHECK(d == 65504); + } + } + + SECTION("infinity") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0x7c})); + json::number_float_t d{j}; + CHECK(!std::isfinite(d)); + CHECK(j.dump() == "null"); + } + + SECTION("NaN") + { + json j = json::from_bjdata(std::vector({'h', 0x00, 0x7e })); + json::number_float_t d{j}; + CHECK(std::isnan(d)); + CHECK(j.dump() == "null"); + } + } + + SECTION("high-precision number") + { + SECTION("unsigned integer number") + { + std::vector vec = {'H', 'i', 0x14, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; + const auto j = json::from_bjdata(vec); + CHECK(j.is_number_unsigned()); + CHECK(j.dump() == "12345678901234567890"); + } + + SECTION("signed integer number") + { + std::vector vec = {'H', 'i', 0x13, '-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'}; + const auto j = json::from_bjdata(vec); + CHECK(j.is_number_integer()); + CHECK(j.dump() == "-123456789012345678"); + } + + SECTION("floating-point number") + { + std::vector vec = {'H', 'i', 0x16, '3', '.', '1', '4', '1', '5', '9', '2', '6', '5', '3', '5', '8', '9', '7', '9', '3', '2', '3', '8', '4', '6'}; + const auto j = json::from_bjdata(vec); + CHECK(j.is_number_float()); + CHECK(j.dump() == "3.141592653589793"); + } + + SECTION("errors") + { + // error while parsing length + std::vector vec0 = {'H', 'i'}; + CHECK(json::from_bjdata(vec0, true, false).is_discarded()); + // error while parsing string + std::vector vec1 = {'H', 'i', '1'}; + CHECK(json::from_bjdata(vec1, true, false).is_discarded()); + + json _; + std::vector vec2 = {'H', 'i', 2, '1', 'A', '3'}; + CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec2), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text: 1A", json::parse_error); + std::vector vec3 = {'H', 'i', 2, '1', '.'}; + CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec3), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text: 1.", json::parse_error); + std::vector vec4 = {'H', 2, '1', '0'}; + CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec4), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x02", json::parse_error); + } + } + } + + SECTION("string") + { + SECTION("N = 0..127") + { + for (size_t N = 0; N <= 127; ++N) + { + CAPTURE(N) + + // create JSON value with string containing of N * 'x' + const auto s = std::string(N, 'x'); + json j = s; + + // create expected byte vector + std::vector expected; + expected.push_back('S'); + expected.push_back('i'); + expected.push_back(static_cast(N)); + for (size_t i = 0; i < N; ++i) + { + expected.push_back('x'); + } + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == N + 3); + // check that no null byte is appended + if (N > 0) + { + CHECK(result.back() != '\x00'); + } + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("N = 128..255") + { + for (size_t N = 128; N <= 255; ++N) + { + CAPTURE(N) + + // create JSON value with string containing of N * 'x' + const auto s = std::string(N, 'x'); + json j = s; + + // create expected byte vector + std::vector expected; + expected.push_back('S'); + expected.push_back('U'); + expected.push_back(static_cast(N)); + for (size_t i = 0; i < N; ++i) + { + expected.push_back('x'); + } + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == N + 3); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("N = 256..32767") + { + for (size_t N : + { + 256u, 999u, 1025u, 3333u, 2048u, 32767u + }) + { + CAPTURE(N) + + // create JSON value with string containing of N * 'x' + const auto s = std::string(N, 'x'); + json j = s; + + // create expected byte vector (hack: create string first) + std::vector expected(N, 'x'); + // reverse order of commands, because we insert at begin() + expected.insert(expected.begin(), static_cast((N >> 8) & 0xff)); + expected.insert(expected.begin(), static_cast(N & 0xff)); + expected.insert(expected.begin(), 'I'); + expected.insert(expected.begin(), 'S'); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == N + 4); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("N = 32768..65535") + { + for (size_t N : + { + 32768u, 55555u, 65535u + }) + { + CAPTURE(N) + + // create JSON value with string containing of N * 'x' + const auto s = std::string(N, 'x'); + json j = s; + + // create expected byte vector (hack: create string first) + std::vector expected(N, 'x'); + // reverse order of commands, because we insert at begin() + expected.insert(expected.begin(), static_cast((N >> 8) & 0xff)); + expected.insert(expected.begin(), static_cast(N & 0xff)); + expected.insert(expected.begin(), 'u'); + expected.insert(expected.begin(), 'S'); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == N + 4); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("N = 65536..2147483647") + { + for (size_t N : + { + 65536u, 77777u, 1048576u + }) + { + CAPTURE(N) + + // create JSON value with string containing of N * 'x' + const auto s = std::string(N, 'x'); + json j = s; + + // create expected byte vector (hack: create string first) + std::vector expected(N, 'x'); + // reverse order of commands, because we insert at begin() + expected.insert(expected.begin(), static_cast((N >> 24) & 0xff)); + expected.insert(expected.begin(), static_cast((N >> 16) & 0xff)); + expected.insert(expected.begin(), static_cast((N >> 8) & 0xff)); + expected.insert(expected.begin(), static_cast(N & 0xff)); + expected.insert(expected.begin(), 'l'); + expected.insert(expected.begin(), 'S'); + + // compare result + size + const auto result = json::to_bjdata(j); + CHECK(result == expected); + CHECK(result.size() == N + 6); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + } + + + SECTION("binary") + { + SECTION("N = 0..127") + { + for (std::size_t N = 0; N <= 127; ++N) + { + CAPTURE(N) + + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json j = json::binary(s); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('[')); + if (N != 0) + { + expected.push_back(static_cast('$')); + expected.push_back(static_cast('U')); + } + expected.push_back(static_cast('#')); + expected.push_back(static_cast('i')); + expected.push_back(static_cast(N)); + for (size_t i = 0; i < N; ++i) + { + expected.push_back(0x78); + } + + // compare result + size + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + if (N == 0) + { + CHECK(result.size() == N + 4); + } + else + { + CHECK(result.size() == N + 6); + } + + // check that no null byte is appended + if (N > 0) + { + CHECK(result.back() != '\x00'); + } + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + + SECTION("N = 128..255") + { + for (std::size_t N = 128; N <= 255; ++N) + { + CAPTURE(N) + + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json j = json::binary(s); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('[')); + expected.push_back(static_cast('$')); + expected.push_back(static_cast('U')); + expected.push_back(static_cast('#')); + expected.push_back(static_cast('U')); + expected.push_back(static_cast(N)); + for (size_t i = 0; i < N; ++i) + { + expected.push_back(0x78); + } + + // compare result + size + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + CHECK(result.size() == N + 6); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + + SECTION("N = 256..32767") + { + for (std::size_t N : + { + 256u, 999u, 1025u, 3333u, 2048u, 32767u + }) + { + CAPTURE(N) + + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json j = json::binary(s); + + // create expected byte vector + std::vector expected(N + 7, 'x'); + expected[0] = '['; + expected[1] = '$'; + expected[2] = 'U'; + expected[3] = '#'; + expected[4] = 'I'; + expected[5] = static_cast(N & 0xFF); + expected[6] = static_cast((N >> 8) & 0xFF); + + // compare result + size + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + CHECK(result.size() == N + 7); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + + SECTION("N = 32768..65535") + { + for (std::size_t N : + { + 32768u, 55555u, 65535u + }) + { + CAPTURE(N) + + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json j = json::binary(s); + + // create expected byte vector + std::vector expected(N + 7, 'x'); + expected[0] = '['; + expected[1] = '$'; + expected[2] = 'U'; + expected[3] = '#'; + expected[4] = 'u'; + expected[5] = static_cast(N & 0xFF); + expected[6] = static_cast((N >> 8) & 0xFF); + + // compare result + size + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + CHECK(result.size() == N + 7); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + + SECTION("N = 65536..2147483647") + { + for (std::size_t N : + { + 65536u, 77777u, 1048576u + }) + { + CAPTURE(N) + + // create JSON value with byte array containing of N * 'x' + const auto s = std::vector(N, 'x'); + json j = json::binary(s); + + // create expected byte vector + std::vector expected(N + 9, 'x'); + expected[0] = '['; + expected[1] = '$'; + expected[2] = 'U'; + expected[3] = '#'; + expected[4] = 'l'; + expected[5] = static_cast(N & 0xFF); + expected[6] = static_cast((N >> 8) & 0xFF); + expected[7] = static_cast((N >> 16) & 0xFF); + expected[8] = static_cast((N >> 24) & 0xFF); + + // compare result + size + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + CHECK(result.size() == N + 9); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + + SECTION("Other Serializations") + { + const std::size_t N = 10; + const auto s = std::vector(N, 'x'); + json j = json::binary(s); + + SECTION("No Count No Type") + { + std::vector expected; + expected.push_back(static_cast('[')); + for (std::size_t i = 0; i < N; ++i) + { + expected.push_back(static_cast('U')); + expected.push_back(static_cast(0x78)); + } + expected.push_back(static_cast(']')); + + // compare result + size + const auto result = json::to_bjdata(j, false, false); + CHECK(result == expected); + CHECK(result.size() == N + 12); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + + SECTION("Yes Count No Type") + { + std::vector expected; + expected.push_back(static_cast('[')); + expected.push_back(static_cast('#')); + expected.push_back(static_cast('i')); + expected.push_back(static_cast(N)); + + for (size_t i = 0; i < N; ++i) + { + expected.push_back(static_cast('U')); + expected.push_back(static_cast(0x78)); + } + + // compare result + size + const auto result = json::to_bjdata(j, true, false); + CHECK(result == expected); + CHECK(result.size() == N + 14); + // check that no null byte is appended + CHECK(result.back() != '\x00'); + + // roundtrip only works to an array of numbers + json j_out = s; + CHECK(json::from_bjdata(result) == j_out); + CHECK(json::from_bjdata(result, true, false) == j_out); + } + } + } + SECTION("array") + { + SECTION("empty") + { + SECTION("size=false type=false") + { + json j = json::array(); + std::vector expected = {'[', ']'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = json::array(); + std::vector expected = {'[', '#', 'i', 0}; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=true") + { + json j = json::array(); + std::vector expected = {'[', '#', 'i', 0}; + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("[null]") + { + SECTION("size=false type=false") + { + json j = {nullptr}; + std::vector expected = {'[', 'Z', ']'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = {nullptr}; + std::vector expected = {'[', '#', 'i', 1, 'Z'}; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=true") + { + json j = {nullptr}; + std::vector expected = {'[', '#', 'i', 1, 'Z'}; + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("[1,2,3,4,5]") + { + SECTION("size=false type=false") + { + json j = json::parse("[1,2,3,4,5]"); + std::vector expected = {'[', 'i', 1, 'i', 2, 'i', 3, 'i', 4, 'i', 5, ']'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = json::parse("[1,2,3,4,5]"); + std::vector expected = {'[', '#', 'i', 5, 'i', 1, 'i', 2, 'i', 3, 'i', 4, 'i', 5}; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=true") + { + json j = json::parse("[1,2,3,4,5]"); + std::vector expected = {'[', '$', 'i', '#', 'i', 5, 1, 2, 3, 4, 5}; + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("[[[[]]]]") + { + SECTION("size=false type=false") + { + json j = json::parse("[[[[]]]]"); + std::vector expected = {'[', '[', '[', '[', ']', ']', ']', ']'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = json::parse("[[[[]]]]"); + std::vector expected = {'[', '#', 'i', 1, '[', '#', 'i', 1, '[', '#', 'i', 1, '[', '#', 'i', 0}; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=true") + { + json j = json::parse("[[[[]]]]"); + std::vector expected = {'[', '#', 'i', 1, '[', '#', 'i', 1, '[', '#', 'i', 1, '[', '#', 'i', 0}; + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("array with int16_t elements") + { + SECTION("size=false type=false") + { + json j(257, nullptr); + std::vector expected(j.size() + 2, 'Z'); // all null + expected[0] = '['; // opening array + expected[258] = ']'; // closing array + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j(257, nullptr); + std::vector expected(j.size() + 5, 'Z'); // all null + expected[0] = '['; // opening array + expected[1] = '#'; // array size + expected[2] = 'I'; // int16 + expected[3] = 0x01; // 0x0101, first byte + expected[4] = 0x01; // 0x0101, second byte + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("array with uint16_t elements") + { + SECTION("size=false type=false") + { + json j(32768, nullptr); + std::vector expected(j.size() + 2, 'Z'); // all null + expected[0] = '['; // opening array + expected[32769] = ']'; // closing array + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j(32768, nullptr); + std::vector expected(j.size() + 5, 'Z'); // all null + expected[0] = '['; // opening array + expected[1] = '#'; // array size + expected[2] = 'u'; // int16 + expected[3] = 0x00; // 0x0101, first byte + expected[4] = 0x80; // 0x0101, second byte + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("array with int32_t elements") + { + SECTION("size=false type=false") + { + json j(65793, nullptr); + std::vector expected(j.size() + 2, 'Z'); // all null + expected[0] = '['; // opening array + expected[65794] = ']'; // closing array + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j(65793, nullptr); + std::vector expected(j.size() + 7, 'Z'); // all null + expected[0] = '['; // opening array + expected[1] = '#'; // array size + expected[2] = 'l'; // int32 + expected[3] = 0x01; // 0x00010101, fourth byte + expected[4] = 0x01; // 0x00010101, third byte + expected[5] = 0x01; // 0x00010101, second byte + expected[6] = 0x00; // 0x00010101, first byte + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + } + + SECTION("object") + { + SECTION("empty") + { + SECTION("size=false type=false") + { + json j = json::object(); + std::vector expected = {'{', '}'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = json::object(); + std::vector expected = {'{', '#', 'i', 0}; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=true") + { + json j = json::object(); + std::vector expected = {'{', '#', 'i', 0}; + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("{\"\":null}") + { + SECTION("size=false type=false") + { + json j = {{"", nullptr}}; + std::vector expected = {'{', 'i', 0, 'Z', '}'}; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = {{"", nullptr}}; + std::vector expected = {'{', '#', 'i', 1, 'i', 0, 'Z'}; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + + SECTION("{\"a\": {\"b\": {\"c\": {}}}}") + { + SECTION("size=false type=false") + { + json j = json::parse(R"({"a": {"b": {"c": {}}}})"); + std::vector expected = + { + '{', 'i', 1, 'a', '{', 'i', 1, 'b', '{', 'i', 1, 'c', '{', '}', '}', '}', '}' + }; + const auto result = json::to_bjdata(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=false") + { + json j = json::parse(R"({"a": {"b": {"c": {}}}})"); + std::vector expected = + { + '{', '#', 'i', 1, 'i', 1, 'a', '{', '#', 'i', 1, 'i', 1, 'b', '{', '#', 'i', 1, 'i', 1, 'c', '{', '#', 'i', 0 + }; + const auto result = json::to_bjdata(j, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + + SECTION("size=true type=true ignore object type marker") + { + json j = json::parse(R"({"a": {"b": {"c": {}}}})"); + std::vector expected = + { + '{', '#', 'i', 1, 'i', 1, 'a', '{', '#', 'i', 1, 'i', 1, 'b', '{', '#', 'i', 1, 'i', 1, 'c', '{', '#', 'i', 0 + }; + const auto result = json::to_bjdata(j, true, true); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bjdata(result) == j); + CHECK(json::from_bjdata(result, true, false) == j); + } + } + } + } + + SECTION("errors") + { + SECTION("strict mode") + { + std::vector vec = {'Z', 'Z'}; + SECTION("non-strict mode") + { + const auto result = json::from_bjdata(vec, false); + CHECK(result == json()); + } + + SECTION("strict mode") + { + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(vec), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vec), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: expected end of input; last byte: 0x5A"); + } + } + } + + SECTION("SAX aborts") + { + SECTION("start_array()") + { + std::vector v = {'[', 'T', 'F', ']'}; + SaxCountdown scp(0); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("start_object()") + { + std::vector v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'}; + SaxCountdown scp(0); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("key() in object") + { + std::vector v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'}; + SaxCountdown scp(1); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("start_array(len)") + { + std::vector v = {'[', '#', 'i', '2', 'T', 'F'}; + SaxCountdown scp(0); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("start_object(len)") + { + std::vector v = {'{', '#', 'i', '1', 3, 'f', 'o', 'o', 'F'}; + SaxCountdown scp(0); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("key() in object with length") + { + std::vector v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'}; + SaxCountdown scp(1); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("start_array() in ndarray _ArraySize_") + { + std::vector v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2}; + SaxCountdown scp(2); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("number_integer() in ndarray _ArraySize_") + { + std::vector v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2}; + SaxCountdown scp(3); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("key() in ndarray _ArrayType_") + { + std::vector v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4}; + SaxCountdown scp(8); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("string() in ndarray _ArrayType_") + { + std::vector v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 3, 2, 6, 5, 4, 3, 2, 1}; + SaxCountdown scp(11); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + + SECTION("start_array() in ndarray _ArrayData_") + { + std::vector v = {'[', '$', 'U', '#', '[', 'i', 2, 'i', 3, ']', 6, 5, 4, 3, 2, 1}; + SaxCountdown scp(13); + CHECK(!json::sax_parse(v, &scp, json::input_format_t::bjdata)); + } + } + + SECTION("parsing values") + { + SECTION("strings") + { + // create a single-character string for all number types + std::vector s_i = {'S', 'i', 1, 'a'}; + std::vector s_U = {'S', 'U', 1, 'a'}; + std::vector s_I = {'S', 'I', 1, 0, 'a'}; + std::vector s_u = {'S', 'u', 1, 0, 'a'}; + std::vector s_l = {'S', 'l', 1, 0, 0, 0, 'a'}; + std::vector s_m = {'S', 'm', 1, 0, 0, 0, 'a'}; + std::vector s_L = {'S', 'L', 1, 0, 0, 0, 0, 0, 0, 0, 'a'}; + std::vector s_M = {'S', 'M', 1, 0, 0, 0, 0, 0, 0, 0, 'a'}; + + // check if string is parsed correctly to "a" + CHECK(json::from_bjdata(s_i) == "a"); + CHECK(json::from_bjdata(s_U) == "a"); + CHECK(json::from_bjdata(s_I) == "a"); + CHECK(json::from_bjdata(s_u) == "a"); + CHECK(json::from_bjdata(s_l) == "a"); + CHECK(json::from_bjdata(s_m) == "a"); + CHECK(json::from_bjdata(s_L) == "a"); + CHECK(json::from_bjdata(s_M) == "a"); + + // roundtrip: output should be optimized + CHECK(json::to_bjdata(json::from_bjdata(s_i)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_U)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_I)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_u)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_l)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_m)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_L)) == s_i); + CHECK(json::to_bjdata(json::from_bjdata(s_M)) == s_i); + } + + SECTION("number") + { + SECTION("float") + { + // float32 + std::vector v_d = {'d', 0xd0, 0x0f, 0x49, 0x40}; + CHECK(json::from_bjdata(v_d) == 3.14159f); + + // float64 + std::vector v_D = {'D', 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40}; + CHECK(json::from_bjdata(v_D) == 3.14159); + + // float32 is serialized as float64 as the library does not support float32 + CHECK(json::to_bjdata(json::from_bjdata(v_d)) == json::to_bjdata(3.14159f)); + } + } + + SECTION("array") + { + SECTION("optimized version (length only)") + { + // create vector with two elements of the same type + std::vector v_TU = {'[', '#', 'U', 2, 'T', 'T'}; + std::vector v_T = {'[', '#', 'i', 2, 'T', 'T'}; + std::vector v_F = {'[', '#', 'i', 2, 'F', 'F'}; + std::vector v_Z = {'[', '#', 'i', 2, 'Z', 'Z'}; + std::vector v_i = {'[', '#', 'i', 2, 'i', 0x7F, 'i', 0x7F}; + std::vector v_U = {'[', '#', 'i', 2, 'U', 0xFF, 'U', 0xFF}; + std::vector v_I = {'[', '#', 'i', 2, 'I', 0xFF, 0x7F, 'I', 0xFF, 0x7F}; + std::vector v_u = {'[', '#', 'i', 2, 'u', 0x0F, 0xA7, 'u', 0x0F, 0xA7}; + std::vector v_l = {'[', '#', 'i', 2, 'l', 0xFF, 0xFF, 0xFF, 0x7F, 'l', 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_m = {'[', '#', 'i', 2, 'm', 0xFF, 0xC9, 0x9A, 0xBB, 'm', 0xFF, 0xC9, 0x9A, 0xBB}; + std::vector v_L = {'[', '#', 'i', 2, 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_M = {'[', '#', 'i', 2, 'M', 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 'M', 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + std::vector v_D = {'[', '#', 'i', 2, 'D', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 'D', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; + std::vector v_S = {'[', '#', 'i', 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; + std::vector v_C = {'[', '#', 'i', 2, 'C', 'a', 'C', 'a'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_TU) == json({true, true})); + CHECK(json::from_bjdata(v_T) == json({true, true})); + CHECK(json::from_bjdata(v_F) == json({false, false})); + CHECK(json::from_bjdata(v_Z) == json({nullptr, nullptr})); + CHECK(json::from_bjdata(v_i) == json({127, 127})); + CHECK(json::from_bjdata(v_U) == json({255, 255})); + CHECK(json::from_bjdata(v_I) == json({32767, 32767})); + CHECK(json::from_bjdata(v_u) == json({42767, 42767})); + CHECK(json::from_bjdata(v_l) == json({2147483647, 2147483647})); + CHECK(json::from_bjdata(v_m) == json({3147483647, 3147483647})); + CHECK(json::from_bjdata(v_L) == json({9223372036854775807, 9223372036854775807})); + CHECK(json::from_bjdata(v_M) == json({10223372036854775807ull, 10223372036854775807ull})); + CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); + CHECK(json::from_bjdata(v_S) == json({"a", "a"})); + CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + + // roundtrip: output should be optimized + CHECK(json::to_bjdata(json::from_bjdata(v_T), true) == v_T); + CHECK(json::to_bjdata(json::from_bjdata(v_F), true) == v_F); + CHECK(json::to_bjdata(json::from_bjdata(v_Z), true) == v_Z); + CHECK(json::to_bjdata(json::from_bjdata(v_i), true) == v_i); + CHECK(json::to_bjdata(json::from_bjdata(v_U), true) == v_U); + CHECK(json::to_bjdata(json::from_bjdata(v_I), true) == v_I); + CHECK(json::to_bjdata(json::from_bjdata(v_u), true) == v_u); + CHECK(json::to_bjdata(json::from_bjdata(v_l), true) == v_l); + CHECK(json::to_bjdata(json::from_bjdata(v_m), true) == v_m); + CHECK(json::to_bjdata(json::from_bjdata(v_L), true) == v_L); + CHECK(json::to_bjdata(json::from_bjdata(v_M), true) == v_M); + CHECK(json::to_bjdata(json::from_bjdata(v_D), true) == v_D); + CHECK(json::to_bjdata(json::from_bjdata(v_S), true) == v_S); + CHECK(json::to_bjdata(json::from_bjdata(v_C), true) == v_S); // char is serialized to string + } + + SECTION("optimized version (type and length)") + { + // create vector with two elements of the same type + std::vector v_i = {'[', '$', 'i', '#', 'i', 2, 0x7F, 0x7F}; + std::vector v_U = {'[', '$', 'U', '#', 'i', 2, 0xFF, 0xFF}; + std::vector v_I = {'[', '$', 'I', '#', 'i', 2, 0xFF, 0x7F, 0xFF, 0x7F}; + std::vector v_u = {'[', '$', 'u', '#', 'i', 2, 0x0F, 0xA7, 0x0F, 0xA7}; + std::vector v_l = {'[', '$', 'l', '#', 'i', 2, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_m = {'[', '$', 'm', '#', 'i', 2, 0xFF, 0xC9, 0x9A, 0xBB, 0xFF, 0xC9, 0x9A, 0xBB}; + std::vector v_L = {'[', '$', 'L', '#', 'i', 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_M = {'[', '$', 'M', '#', 'i', 2, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + std::vector v_D = {'[', '$', 'D', '#', 'i', 2, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; + std::vector v_S = {'[', '#', 'i', 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; + std::vector v_C = {'[', '$', 'C', '#', 'i', 2, 'a', 'a'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_i) == json({127, 127})); + CHECK(json::from_bjdata(v_U) == json({255, 255})); + CHECK(json::from_bjdata(v_I) == json({32767, 32767})); + CHECK(json::from_bjdata(v_u) == json({42767, 42767})); + CHECK(json::from_bjdata(v_l) == json({2147483647, 2147483647})); + CHECK(json::from_bjdata(v_m) == json({3147483647, 3147483647})); + CHECK(json::from_bjdata(v_L) == json({9223372036854775807, 9223372036854775807})); + CHECK(json::from_bjdata(v_M) == json({10223372036854775807ull, 10223372036854775807ull})); + CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); + CHECK(json::from_bjdata(v_S) == json({"a", "a"})); + CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + + // roundtrip: output should be optimized + std::vector v_empty = {'[', '#', 'i', 0}; + CHECK(json::to_bjdata(json::from_bjdata(v_i), true, true) == v_i); + CHECK(json::to_bjdata(json::from_bjdata(v_U), true, true) == v_U); + CHECK(json::to_bjdata(json::from_bjdata(v_I), true, true) == v_I); + CHECK(json::to_bjdata(json::from_bjdata(v_u), true, true) == v_u); + CHECK(json::to_bjdata(json::from_bjdata(v_l), true, true) == v_l); + CHECK(json::to_bjdata(json::from_bjdata(v_m), true, true) == v_m); + CHECK(json::to_bjdata(json::from_bjdata(v_L), true, true) == v_L); + CHECK(json::to_bjdata(json::from_bjdata(v_M), true, true) == v_M); + CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); + CHECK(json::to_bjdata(json::from_bjdata(v_S), true, true) == v_S); + CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_S); // char is serialized to string + } + + SECTION("optimized ndarray (type and vector-size as optimized 1D array)") + { + // create vector with two elements of the same type + std::vector v_0 = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 1, 0}; + std::vector v_1 = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 1, 2, 0x7F, 0x7F}; + std::vector v_i = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0x7F, 0x7F}; + std::vector v_U = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0xFF}; + std::vector v_I = {'[', '$', 'I', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0x7F, 0xFF, 0x7F}; + std::vector v_u = {'[', '$', 'u', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0x0F, 0xA7, 0x0F, 0xA7}; + std::vector v_l = {'[', '$', 'l', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_m = {'[', '$', 'm', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0xC9, 0x9A, 0xBB, 0xFF, 0xC9, 0x9A, 0xBB}; + std::vector v_L = {'[', '$', 'L', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_M = {'[', '$', 'M', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + std::vector v_D = {'[', '$', 'D', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; + std::vector v_S = {'[', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; + std::vector v_C = {'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 1, 2, 'a', 'a'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_0) == json::array()); + CHECK(json::from_bjdata(v_1) == json({127, 127})); + CHECK(json::from_bjdata(v_i) == json({127, 127})); + CHECK(json::from_bjdata(v_U) == json({255, 255})); + CHECK(json::from_bjdata(v_I) == json({32767, 32767})); + CHECK(json::from_bjdata(v_u) == json({42767, 42767})); + CHECK(json::from_bjdata(v_l) == json({2147483647, 2147483647})); + CHECK(json::from_bjdata(v_m) == json({3147483647, 3147483647})); + CHECK(json::from_bjdata(v_L) == json({9223372036854775807, 9223372036854775807})); + CHECK(json::from_bjdata(v_M) == json({10223372036854775807ull, 10223372036854775807ull})); + CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); + CHECK(json::from_bjdata(v_S) == json({"a", "a"})); + CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + } + + SECTION("optimized ndarray (type and vector-size ndarray with JData annotations)") + { + // create vector with 0, 1, 2 elements of the same type + std::vector v_e = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 0xFE, 0xFF}; + std::vector v_U = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + std::vector v_i = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + std::vector v_u = {'[', '$', 'u', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00}; + std::vector v_I = {'[', '$', 'I', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00}; + std::vector v_m = {'[', '$', 'm', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}; + std::vector v_l = {'[', '$', 'l', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}; + std::vector v_M = {'[', '$', 'M', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector v_L = {'[', '$', 'L', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector v_d = {'[', '$', 'd', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0xA0, 0x40, 0x00, 0x00, 0xC0, 0x40}; + std::vector v_D = {'[', '$', 'D', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40}; + std::vector v_C = {'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 'a', 'b', 'c', 'd', 'e', 'f'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_e) == json({{"_ArrayData_", {254, 255}}, {"_ArraySize_", {2, 1}}, {"_ArrayType_", "uint8"}})); + CHECK(json::from_bjdata(v_U) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint8"}})); + CHECK(json::from_bjdata(v_i) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int8"}})); + CHECK(json::from_bjdata(v_i) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int8"}})); + CHECK(json::from_bjdata(v_u) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint16"}})); + CHECK(json::from_bjdata(v_I) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int16"}})); + CHECK(json::from_bjdata(v_m) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint32"}})); + CHECK(json::from_bjdata(v_l) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int32"}})); + CHECK(json::from_bjdata(v_M) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint64"}})); + CHECK(json::from_bjdata(v_L) == json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "int64"}})); + CHECK(json::from_bjdata(v_d) == json({{"_ArrayData_", {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "single"}})); + CHECK(json::from_bjdata(v_D) == json({{"_ArrayData_", {1., 2., 3., 4., 5., 6.}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "double"}})); + CHECK(json::from_bjdata(v_C) == json({{"_ArrayData_", {'a', 'b', 'c', 'd', 'e', 'f'}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "char"}})); + + // roundtrip: output should be optimized + CHECK(json::to_bjdata(json::from_bjdata(v_e), true, true) == v_e); + CHECK(json::to_bjdata(json::from_bjdata(v_U), true, true) == v_U); + CHECK(json::to_bjdata(json::from_bjdata(v_i), true, true) == v_i); + CHECK(json::to_bjdata(json::from_bjdata(v_u), true, true) == v_u); + CHECK(json::to_bjdata(json::from_bjdata(v_I), true, true) == v_I); + CHECK(json::to_bjdata(json::from_bjdata(v_m), true, true) == v_m); + CHECK(json::to_bjdata(json::from_bjdata(v_l), true, true) == v_l); + CHECK(json::to_bjdata(json::from_bjdata(v_M), true, true) == v_M); + CHECK(json::to_bjdata(json::from_bjdata(v_L), true, true) == v_L); + CHECK(json::to_bjdata(json::from_bjdata(v_d), true, true) == v_d); + CHECK(json::to_bjdata(json::from_bjdata(v_D), true, true) == v_D); + CHECK(json::to_bjdata(json::from_bjdata(v_C), true, true) == v_C); + } + + SECTION("optimized ndarray (type and vector-size as 1D array)") + { + // create vector with two elements of the same type + std::vector v_0 = {'[', '$', 'i', '#', '[', ']'}; + std::vector v_i = {'[', '$', 'i', '#', '[', 'i', 1, 'i', 2, ']', 0x7F, 0x7F}; + std::vector v_U = {'[', '$', 'U', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xFF}; + std::vector v_I = {'[', '$', 'I', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0x7F, 0xFF, 0x7F}; + std::vector v_u = {'[', '$', 'u', '#', '[', 'i', 1, 'i', 2, ']', 0x0F, 0xA7, 0x0F, 0xA7}; + std::vector v_l = {'[', '$', 'l', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_m = {'[', '$', 'm', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xC9, 0x9A, 0xBB, 0xFF, 0xC9, 0x9A, 0xBB}; + std::vector v_L = {'[', '$', 'L', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_M = {'[', '$', 'M', '#', '[', 'i', 1, 'i', 2, ']', 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + std::vector v_D = {'[', '$', 'D', '#', '[', 'i', 1, 'i', 2, ']', 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; + std::vector v_S = {'[', '#', '[', 'i', 1, 'i', 2, ']', 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; + std::vector v_C = {'[', '$', 'C', '#', '[', 'i', 1, 'i', 2, ']', 'a', 'a'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_0) == json::array()); + CHECK(json::from_bjdata(v_i) == json({127, 127})); + CHECK(json::from_bjdata(v_U) == json({255, 255})); + CHECK(json::from_bjdata(v_I) == json({32767, 32767})); + CHECK(json::from_bjdata(v_u) == json({42767, 42767})); + CHECK(json::from_bjdata(v_l) == json({2147483647, 2147483647})); + CHECK(json::from_bjdata(v_m) == json({3147483647, 3147483647})); + CHECK(json::from_bjdata(v_L) == json({9223372036854775807, 9223372036854775807})); + CHECK(json::from_bjdata(v_M) == json({10223372036854775807ull, 10223372036854775807ull})); + CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); + CHECK(json::from_bjdata(v_S) == json({"a", "a"})); + CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + } + + SECTION("optimized ndarray (type and vector-size as size-optimized array)") + { + // create vector with two elements of the same type + std::vector v_i = {'[', '$', 'i', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0x7F, 0x7F}; + std::vector v_U = {'[', '$', 'U', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0xFF}; + std::vector v_I = {'[', '$', 'I', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0x7F, 0xFF, 0x7F}; + std::vector v_u = {'[', '$', 'u', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0x0F, 0xA7, 0x0F, 0xA7}; + std::vector v_l = {'[', '$', 'l', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_m = {'[', '$', 'm', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0xC9, 0x9A, 0xBB, 0xFF, 0xC9, 0x9A, 0xBB}; + std::vector v_L = {'[', '$', 'L', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; + std::vector v_M = {'[', '$', 'M', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + std::vector v_D = {'[', '$', 'D', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40, 0x4a, 0xd8, 0x12, 0x4d, 0xfb, 0x21, 0x09, 0x40}; + std::vector v_S = {'[', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 'S', 'i', 1, 'a', 'S', 'i', 1, 'a'}; + std::vector v_C = {'[', '$', 'C', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2, 'a', 'a'}; + + // check if vector is parsed correctly + CHECK(json::from_bjdata(v_i) == json({127, 127})); + CHECK(json::from_bjdata(v_U) == json({255, 255})); + CHECK(json::from_bjdata(v_I) == json({32767, 32767})); + CHECK(json::from_bjdata(v_u) == json({42767, 42767})); + CHECK(json::from_bjdata(v_l) == json({2147483647, 2147483647})); + CHECK(json::from_bjdata(v_m) == json({3147483647, 3147483647})); + CHECK(json::from_bjdata(v_L) == json({9223372036854775807, 9223372036854775807})); + CHECK(json::from_bjdata(v_M) == json({10223372036854775807ull, 10223372036854775807ull})); + CHECK(json::from_bjdata(v_D) == json({3.1415926, 3.1415926})); + CHECK(json::from_bjdata(v_S) == json({"a", "a"})); + CHECK(json::from_bjdata(v_C) == json({"a", "a"})); + } + + SECTION("invalid ndarray annotations remains as object") + { + // check if invalid ND array annotations stay as object + json j_type = json({{"_ArrayData_", {1, 2, 3, 4, 5, 6}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "invalidtype"}}); + json j_size = json({{"_ArrayData_", {1, 2, 3, 4, 5}}, {"_ArraySize_", {2, 3}}, {"_ArrayType_", "uint8"}}); + + // roundtrip: output should stay as object + CHECK(json::from_bjdata(json::to_bjdata(j_type), true, true) == j_type); + CHECK(json::from_bjdata(json::to_bjdata(j_size), true, true) == j_size); + } + + SECTION("do not accept NTFZ markers in ndarray optimized type") + { + json _; + std::vector v_N = {'[', '$', 'N', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2}; + std::vector v_T = {'[', '$', 'T', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2}; + std::vector v_F = {'[', '$', 'F', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2}; + std::vector v_Z = {'[', '$', 'Z', '#', '[', '#', 'i', 2, 'i', 1, 'i', 2}; + + CHECK(json::from_bjdata(v_N, true, true).is_discarded()); + CHECK(json::from_bjdata(v_T, true, true).is_discarded()); + CHECK(json::from_bjdata(v_F, true, true).is_discarded()); + CHECK(json::from_bjdata(v_Z, true, true).is_discarded()); + } + + SECTION("do not accept NTFZ markers in ndarray optimized type") + { + json _; + std::vector v_N = {'[', '$', 'N', '#', '[', 'i', 1, 'i', 2, ']'}; + std::vector v_T = {'[', '$', 'T', '#', '[', 'i', 1, 'i', 2, ']'}; + std::vector v_F = {'[', '$', 'F', '#', '[', 'i', 1, 'i', 2, ']'}; + std::vector v_Z = {'[', '$', 'Z', '#', '[', 'i', 1, 'i', 2, ']'}; + + CHECK(json::from_bjdata(v_N, true, true).is_discarded()); + CHECK(json::from_bjdata(v_T, true, true).is_discarded()); + CHECK(json::from_bjdata(v_F, true, true).is_discarded()); + CHECK(json::from_bjdata(v_Z, true, true).is_discarded()); + } + } + } + + SECTION("parse errors") + { + SECTION("empty byte vector") + { + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(std::vector()), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(std::vector()), + "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing BJData value: unexpected end of input"); + } + + SECTION("char") + { + SECTION("eof after C byte") + { + std::vector v = {'C'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData char: unexpected end of input"); + } + + SECTION("byte out of range") + { + std::vector v = {'C', 130}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82"); + } + } + + SECTION("strings") + { + SECTION("eof after S byte") + { + std::vector v = {'S'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: unexpected end of input"); + } + + SECTION("invalid byte") + { + std::vector v = {'S', '1', 'a'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31"); + } + + SECTION("parse bjdata markers in ubjson") + { + // create a single-character string for all number types + std::vector s_u = {'S', 'u', 1, 0, 'a'}; + std::vector s_m = {'S', 'm', 1, 0, 0, 0, 'a'}; + std::vector s_M = {'S', 'M', 1, 0, 0, 0, 0, 0, 0, 0, 'a'}; + + json _; + // check if string is parsed correctly to "a" + CHECK_THROWS_AS(_ = json::from_ubjson(s_u), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(s_u), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x75"); + + CHECK_THROWS_AS(_ = json::from_ubjson(s_m), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(s_m), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x6D"); + + CHECK_THROWS_AS(_ = json::from_ubjson(s_M), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(s_M), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x4D"); + } + } + + SECTION("array") + { + SECTION("optimized array: no size following type") + { + std::vector v = {'[', '$', 'i', 2}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BJData size: expected '#' after type information; last byte: 0x02"); + } + } + + SECTION("strings") + { + std::vector vS = {'S'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vS, true, false).is_discarded()); + + std::vector v = {'S', 'i', '2', 'a'}; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing BJData string: unexpected end of input"); + CHECK(json::from_bjdata(v, true, false).is_discarded()); + + std::vector vC = {'C'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vC), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vC), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing BJData char: unexpected end of input"); + CHECK(json::from_bjdata(vC, true, false).is_discarded()); + } + + SECTION("sizes") + { + std::vector vU = {'[', '#', 'U'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(vU), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vU, true, false).is_discarded()); + + std::vector vi = {'[', '#', 'i'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vi), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vi, true, false).is_discarded()); + + std::vector vI = {'[', '#', 'I'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vI), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vI), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vI, true, false).is_discarded()); + + std::vector vu = {'[', '#', 'u'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vu), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vu), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vu, true, false).is_discarded()); + + std::vector vl = {'[', '#', 'l'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vl), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vl), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vl, true, false).is_discarded()); + + std::vector vm = {'[', '#', 'm'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vm), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vm), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vm, true, false).is_discarded()); + + std::vector vL = {'[', '#', 'L'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vL), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vL), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vL, true, false).is_discarded()); + + std::vector vM = {'[', '#', 'M'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vM), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vM), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vM, true, false).is_discarded()); + + std::vector v0 = {'[', '#', 'T', ']'}; + CHECK_THROWS_AS(_ = json::from_bjdata(v0), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x54"); + CHECK(json::from_bjdata(v0, true, false).is_discarded()); + } + + SECTION("parse bjdata markers as array size in ubjson") + { + json _; + std::vector vu = {'[', '#', 'u'}; + CHECK_THROWS_AS(_ = json::from_ubjson(vu), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(vu), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x75"); + CHECK(json::from_ubjson(vu, true, false).is_discarded()); + + std::vector vm = {'[', '#', 'm'}; + CHECK_THROWS_AS(_ = json::from_ubjson(vm), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(vm), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x6D"); + CHECK(json::from_ubjson(vm, true, false).is_discarded()); + + std::vector vM = {'[', '#', 'M'}; + CHECK_THROWS_AS(_ = json::from_ubjson(vM), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(vM), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x4D"); + CHECK(json::from_ubjson(vM, true, false).is_discarded()); + + std::vector v0 = {'[', '#', '['}; + CHECK_THROWS_AS(_ = json::from_ubjson(v0), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_ubjson(v0), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x5B"); + CHECK(json::from_ubjson(v0, true, false).is_discarded()); + } + + SECTION("types") + { + std::vector v0 = {'[', '$'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(v0), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v0), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing BJData type: unexpected end of input"); + CHECK(json::from_bjdata(v0, true, false).is_discarded()); + + std::vector vi = {'[', '$', '#'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vi), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vi, true, false).is_discarded()); + + std::vector vU = {'[', '$', 'U'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vU), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vU, true, false).is_discarded()); + } + + SECTION("arrays") + { + std::vector vST = {'[', '$', 'i', '#', 'i', 2, 1}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(vST), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vST), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vST, true, false).is_discarded()); + + std::vector vS = {'[', '#', 'i', 2, 'i', 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vS, true, false).is_discarded()); + + std::vector v = {'[', 'i', 2, 'i', 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(v, true, false).is_discarded()); + } + + SECTION("ndarrays") + { + std::vector vST = {'[', '$', 'i', '#', '[', '$', 'i', '#'}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(vST), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vST), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0xFF"); + CHECK(json::from_bjdata(vST, true, false).is_discarded()); + + std::vector v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 1, 2}; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 13: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(v, true, false).is_discarded()); + + std::vector vS0 = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(vS0), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vS0), "[json.exception.parse_error.110] parse error at byte 12: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vS0, true, false).is_discarded()); + + std::vector vS = {'[', '$', 'i', '#', '[', '#', 'i', 2, 1, 2, 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x01"); + CHECK(json::from_bjdata(vS, true, false).is_discarded()); + + std::vector vT = {'[', '$', 'i', '#', '[', 'i', 2, 'i'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vT), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vT), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vT, true, false).is_discarded()); + + std::vector vT0 = {'[', '$', 'i', '#', '[', 'i'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vT0), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vT0), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vT0, true, false).is_discarded()); + + std::vector vu = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'u', 1, 0}; + CHECK_THROWS_AS(_ = json::from_bjdata(vu), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vu), "[json.exception.parse_error.110] parse error at byte 12: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vu, true, false).is_discarded()); + + std::vector vm = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'm', 1, 0, 0, 0}; + CHECK_THROWS_AS(_ = json::from_bjdata(vm), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vm), "[json.exception.parse_error.110] parse error at byte 14: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vm, true, false).is_discarded()); + + std::vector vM = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'M', 1, 0, 0, 0, 0, 0, 0, 0}; + CHECK_THROWS_AS(_ = json::from_bjdata(vM), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vM), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vM, true, false).is_discarded()); + + std::vector vU = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 3, 1, 2, 3, 4, 5}; + CHECK_THROWS_AS(_ = json::from_bjdata(vU), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vU), "[json.exception.parse_error.110] parse error at byte 18: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vU, true, false).is_discarded()); + + std::vector vT1 = {'[', '$', 'T', '#', '[', '$', 'i', '#', 'i', 2, 2, 3}; + CHECK(json::from_bjdata(vT1, true, false).is_discarded()); + + std::vector vh = {'[', '$', 'h', '#', '[', '$', 'i', '#', 'i', 2, 2, 3}; + CHECK(json::from_bjdata(vh, true, false).is_discarded()); + } + + SECTION("objects") + { + std::vector vST = {'{', '$', 'i', '#', 'i', 2, 'i', 1, 'a', 1}; + json _; + CHECK_THROWS_AS(_ = json::from_bjdata(vST), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vST), "[json.exception.parse_error.110] parse error at byte 11: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vST, true, false).is_discarded()); + + std::vector vT = {'{', '$', 'i', 'i', 1, 'a', 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(vT), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vT), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BJData size: expected '#' after type information; last byte: 0x69"); + CHECK(json::from_bjdata(vT, true, false).is_discarded()); + + std::vector vS = {'{', '#', 'i', 2, 'i', 1, 'a', 'i', 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(vS), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vS), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vS, true, false).is_discarded()); + + std::vector v = {'{', 'i', 1, 'a', 'i', 1}; + CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(v, true, false).is_discarded()); + + std::vector v2 = {'{', 'i', 1, 'a', 'i', 1, 'i'}; + CHECK_THROWS_AS(_ = json::from_bjdata(v2), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(v2, true, false).is_discarded()); + + std::vector v3 = {'{', 'i', 1, 'a'}; + CHECK_THROWS_AS(_ = json::from_bjdata(v3), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(v3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(v3, true, false).is_discarded()); + + std::vector vST1 = {'{', '$', 'd', '#', 'i', 2, 'i', 1, 'a'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vST1), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vST1), "[json.exception.parse_error.110] parse error at byte 10: syntax error while parsing BJData number: unexpected end of input"); + CHECK(json::from_bjdata(vST1, true, false).is_discarded()); + + std::vector vST2 = {'{', '#', 'i', 2, 'i', 1, 'a'}; + CHECK_THROWS_AS(_ = json::from_bjdata(vST2), json::parse_error&); + CHECK_THROWS_WITH(_ = json::from_bjdata(vST2), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BJData value: unexpected end of input"); + CHECK(json::from_bjdata(vST2, true, false).is_discarded()); + + std::vector vO = {'{', '#', '[', 'i', 2, 'i', 1, ']', 'i', 1, 'a', 'i', 1, 'i', 1, 'b', 'i', 2}; + CHECK(json::from_bjdata(vO, true, false).is_discarded()); + } + } + + SECTION("writing optimized values") + { + SECTION("integer") + { + SECTION("array of i") + { + json j = {1, -1}; + std::vector expected = {'[', '$', 'i', '#', 'i', 2, 1, 0xff}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + + SECTION("array of U") + { + json j = {200, 201}; + std::vector expected = {'[', '$', 'U', '#', 'i', 2, 0xC8, 0xC9}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + + SECTION("array of I") + { + json j = {30000, -30000}; + std::vector expected = {'[', '$', 'I', '#', 'i', 2, 0x30, 0x75, 0xd0, 0x8a}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + + SECTION("array of u") + { + json j = {50000, 50001}; + std::vector expected = {'[', '$', 'u', '#', 'i', 2, 0x50, 0xC3, 0x51, 0xC3}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + + SECTION("array of l") + { + json j = {70000, -70000}; + std::vector expected = {'[', '$', 'l', '#', 'i', 2, 0x70, 0x11, 0x01, 0x00, 0x90, 0xEE, 0xFE, 0xFF}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + + SECTION("array of m") + { + json j = {3147483647, 3147483648}; + std::vector expected = {'[', '$', 'm', '#', 'i', 2, 0xFF, 0xC9, 0x9A, 0xBB, 0x00, 0xCA, 0x9A, 0xBB}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + + SECTION("array of L") + { + json j = {5000000000, -5000000000}; + std::vector expected = {'[', '$', 'L', '#', 'i', 2, 0x00, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xFA, 0xD5, 0xFE, 0xFF, 0xFF, 0xFF}; + CHECK(json::to_bjdata(j, true, true) == expected); + } + } + + SECTION("unsigned integer") + { + SECTION("array of i") + { + json j = {1u, 2u}; + std::vector expected = {'[', '$', 'i', '#', 'i', 2, 1, 2}; + std::vector expected_size = {'[', '#', 'i', 2, 'i', 1, 'i', 2}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of U") + { + json j = {200u, 201u}; + std::vector expected = {'[', '$', 'U', '#', 'i', 2, 0xC8, 0xC9}; + std::vector expected_size = {'[', '#', 'i', 2, 'U', 0xC8, 'U', 0xC9}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of I") + { + json j = {30000u, 30001u}; + std::vector expected = {'[', '$', 'I', '#', 'i', 2, 0x30, 0x75, 0x31, 0x75}; + std::vector expected_size = {'[', '#', 'i', 2, 'I', 0x30, 0x75, 'I', 0x31, 0x75}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of u") + { + json j = {50000u, 50001u}; + std::vector expected = {'[', '$', 'u', '#', 'i', 2, 0x50, 0xC3, 0x51, 0xC3}; + std::vector expected_size = {'[', '#', 'i', 2, 'u', 0x50, 0xC3, 'u', 0x51, 0xC3}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of l") + { + json j = {70000u, 70001u}; + std::vector expected = {'[', '$', 'l', '#', 'i', 2, 0x70, 0x11, 0x01, 0x00, 0x71, 0x11, 0x01, 0x00}; + std::vector expected_size = {'[', '#', 'i', 2, 'l', 0x70, 0x11, 0x01, 0x00, 'l', 0x71, 0x11, 0x01, 0x00}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of m") + { + json j = {3147483647u, 3147483648u}; + std::vector expected = {'[', '$', 'm', '#', 'i', 2, 0xFF, 0xC9, 0x9A, 0xBB, 0x00, 0xCA, 0x9A, 0xBB}; + std::vector expected_size = {'[', '#', 'i', 2, 'm', 0xFF, 0xC9, 0x9A, 0xBB, 'm', 0x00, 0xCA, 0x9A, 0xBB}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of L") + { + json j = {5000000000u, 5000000001u}; + std::vector expected = {'[', '$', 'L', '#', 'i', 2, 0x00, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00, 0x01, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00}; + std::vector expected_size = {'[', '#', 'i', 2, 'L', 0x00, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00, 'L', 0x01, 0xF2, 0x05, 0x2A, 0x01, 0x00, 0x00, 0x00}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + + SECTION("array of M") + { + json j = {10223372036854775807ull, 10223372036854775808ull}; + std::vector expected = {'[', '$', 'M', '#', 'i', 2, 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 0x00, 0x00, 0x64, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + std::vector expected_size = {'[', '#', 'i', 2, 'M', 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, 'M', 0x00, 0x00, 0x64, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D}; + CHECK(json::to_bjdata(j, true, true) == expected); + CHECK(json::to_bjdata(j, true) == expected_size); + } + } + } +} + +TEST_CASE("Universal Binary JSON Specification Examples 1") +{ + SECTION("Null Value") + { + json j = {{"passcode", nullptr}}; + std::vector v = {'{', 'i', 8, 'p', 'a', 's', 's', 'c', 'o', 'd', 'e', 'Z', '}'}; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("No-Op Value") + { + json j = {"foo", "bar", "baz"}; + std::vector v = {'[', 'S', 'i', 3, 'f', 'o', 'o', + 'S', 'i', 3, 'b', 'a', 'r', + 'S', 'i', 3, 'b', 'a', 'z', ']' + }; + std::vector v2 = {'[', 'S', 'i', 3, 'f', 'o', 'o', 'N', + 'S', 'i', 3, 'b', 'a', 'r', 'N', 'N', 'N', + 'S', 'i', 3, 'b', 'a', 'z', 'N', 'N', ']' + }; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + CHECK(json::from_bjdata(v2) == j); + } + + SECTION("Boolean Types") + { + json j = {{"authorized", true}, {"verified", false}}; + std::vector v = {'{', 'i', 10, 'a', 'u', 't', 'h', 'o', 'r', 'i', 'z', 'e', 'd', 'T', + 'i', 8, 'v', 'e', 'r', 'i', 'f', 'i', 'e', 'd', 'F', '}' + }; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Numeric Types") + { + json j = + { + {"int8", 16}, + {"uint8", 255}, + {"int16", 32767}, + {"uint16", 42767}, + {"int32", 2147483647}, + {"uint32", 3147483647}, + {"int64", 9223372036854775807}, + {"uint64", 10223372036854775807ull}, + {"float64", 113243.7863123} + }; + std::vector v = {'{', + 'i', 7, 'f', 'l', 'o', 'a', 't', '6', '4', 'D', 0xcf, 0x34, 0xbc, 0x94, 0xbc, 0xa5, 0xfb, 0x40, + 'i', 5, 'i', 'n', 't', '1', '6', 'I', 0xff, 0x7f, + 'i', 5, 'i', 'n', 't', '3', '2', 'l', 0xff, 0xff, 0xff, 0x7f, + 'i', 5, 'i', 'n', 't', '6', '4', 'L', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 'i', 4, 'i', 'n', 't', '8', 'i', 16, + 'i', 6, 'u', 'i', 'n', 't', '1', '6', 'u', 0x0F, 0xA7, + 'i', 6, 'u', 'i', 'n', 't', '3', '2', 'm', 0xFF, 0xC9, 0x9A, 0xBB, + 'i', 6, 'u', 'i', 'n', 't', '6', '4', 'M', 0xFF, 0xFF, 0x63, 0xA7, 0xB3, 0xB6, 0xE0, 0x8D, + 'i', 5, 'u', 'i', 'n', 't', '8', 'U', 0xff, + '}' + }; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Char Type") + { + json j = {{"rolecode", "a"}, {"delim", ";"}}; + std::vector v = {'{', 'i', 5, 'd', 'e', 'l', 'i', 'm', 'C', ';', 'i', 8, 'r', 'o', 'l', 'e', 'c', 'o', 'd', 'e', 'C', 'a', '}'}; + //CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("String Type") + { + SECTION("English") + { + json j = "hello"; + std::vector v = {'S', 'i', 5, 'h', 'e', 'l', 'l', 'o'}; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Russian") + { + json j = "привет"; + std::vector v = {'S', 'i', 12, 0xD0, 0xBF, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, 0xD1, 0x82}; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Russian") + { + json j = "مرحبا"; + std::vector v = {'S', 'i', 10, 0xD9, 0x85, 0xD8, 0xB1, 0xD8, 0xAD, 0xD8, 0xA8, 0xD8, 0xA7}; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + } + + SECTION("Array Type") + { + SECTION("size=false type=false") + { + // note the float has been replaced by a double + json j = {nullptr, true, false, 4782345193, 153.132, "ham"}; + std::vector v = {'[', 'Z', 'T', 'F', 'L', 0xE9, 0xCB, 0x0C, 0x1D, 0x01, 0x00, 0x00, 0x00, 'D', 0x4e, 0x62, 0x10, 0x58, 0x39, 0x24, 0x63, 0x40, 'S', 'i', 3, 'h', 'a', 'm', ']'}; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("size=true type=false") + { + // note the float has been replaced by a double + json j = {nullptr, true, false, 4782345193, 153.132, "ham"}; + std::vector v = {'[', '#', 'i', 6, 'Z', 'T', 'F', 'L', 0xE9, 0xCB, 0x0C, 0x1D, 0x01, 0x00, 0x00, 0x00, 'D', 0x4e, 0x62, 0x10, 0x58, 0x39, 0x24, 0x63, 0x40, 'S', 'i', 3, 'h', 'a', 'm'}; + CHECK(json::to_bjdata(j, true) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("size=true type=true") + { + // note the float has been replaced by a double + json j = {nullptr, true, false, 4782345193, 153.132, "ham"}; + std::vector v = {'[', '#', 'i', 6, 'Z', 'T', 'F', 'L', 0xE9, 0xCB, 0x0C, 0x1D, 0x01, 0x00, 0x00, 0x00, 'D', 0x4e, 0x62, 0x10, 0x58, 0x39, 0x24, 0x63, 0x40, 'S', 'i', 3, 'h', 'a', 'm'}; + CHECK(json::to_bjdata(j, true, true) == v); + CHECK(json::from_bjdata(v) == j); + } + } + + SECTION("Object Type") + { + SECTION("size=false type=false") + { + json j = + { + { + "post", { + {"id", 1137}, + {"author", "rkalla"}, + {"timestamp", 1364482090592}, + {"body", "I totally agree!"} + } + } + }; + std::vector v = {'{', 'i', 4, 'p', 'o', 's', 't', '{', + 'i', 6, 'a', 'u', 't', 'h', 'o', 'r', 'S', 'i', 6, 'r', 'k', 'a', 'l', 'l', 'a', + 'i', 4, 'b', 'o', 'd', 'y', 'S', 'i', 16, 'I', ' ', 't', 'o', 't', 'a', 'l', 'l', 'y', ' ', 'a', 'g', 'r', 'e', 'e', '!', + 'i', 2, 'i', 'd', 'I', 0x71, 0x04, + 'i', 9, 't', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', 'L', 0x60, 0x66, 0x78, 0xB1, 0x3D, 0x01, 0x00, 0x00, + '}', '}' + }; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("size=true type=false") + { + json j = + { + { + "post", { + {"id", 1137}, + {"author", "rkalla"}, + {"timestamp", 1364482090592}, + {"body", "I totally agree!"} + } + } + }; + std::vector v = {'{', '#', 'i', 1, 'i', 4, 'p', 'o', 's', 't', '{', '#', 'i', 4, + 'i', 6, 'a', 'u', 't', 'h', 'o', 'r', 'S', 'i', 6, 'r', 'k', 'a', 'l', 'l', 'a', + 'i', 4, 'b', 'o', 'd', 'y', 'S', 'i', 16, 'I', ' ', 't', 'o', 't', 'a', 'l', 'l', 'y', ' ', 'a', 'g', 'r', 'e', 'e', '!', + 'i', 2, 'i', 'd', 'I', 0x71, 0x04, + 'i', 9, 't', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', 'L', 0x60, 0x66, 0x78, 0xB1, 0x3D, 0x01, 0x00, 0x00, + }; + CHECK(json::to_bjdata(j, true) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("size=true type=true") + { + json j = + { + { + "post", { + {"id", 1137}, + {"author", "rkalla"}, + {"timestamp", 1364482090592}, + {"body", "I totally agree!"} + } + } + }; + std::vector v = {'{', '#', 'i', 1, 'i', 4, 'p', 'o', 's', 't', '{', '#', 'i', 4, + 'i', 6, 'a', 'u', 't', 'h', 'o', 'r', 'S', 'i', 6, 'r', 'k', 'a', 'l', 'l', 'a', + 'i', 4, 'b', 'o', 'd', 'y', 'S', 'i', 16, 'I', ' ', 't', 'o', 't', 'a', 'l', 'l', 'y', ' ', 'a', 'g', 'r', 'e', 'e', '!', + 'i', 2, 'i', 'd', 'I', 0x71, 0x04, + 'i', 9, 't', 'i', 'm', 'e', 's', 't', 'a', 'm', 'p', 'L', 0x60, 0x66, 0x78, 0xB1, 0x3D, 0x01, 0x00, 0x00, + }; + CHECK(json::to_bjdata(j, true, true) == v); + CHECK(json::from_bjdata(v) == j); + } + } + + SECTION("Optimized Format") + { + SECTION("Array Example") + { + SECTION("No Optimization") + { + // note the floats have been replaced by doubles + json j = {29.97, 31.13, 67.0, 2.113, 23.888}; + std::vector v = {'[', + 'D', 0xb8, 0x1e, 0x85, 0xeb, 0x51, 0xf8, 0x3d, 0x40, + 'D', 0xe1, 0x7a, 0x14, 0xae, 0x47, 0x21, 0x3f, 0x40, + 'D', 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x50, 0x40, + 'D', 0x81, 0x95, 0x43, 0x8b, 0x6c, 0xe7, 0x00, 0x40, + 'D', 0x17, 0xd9, 0xce, 0xf7, 0x53, 0xe3, 0x37, 0x40, + ']' + }; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Optimized with count") + { + // note the floats have been replaced by doubles + json j = {29.97, 31.13, 67.0, 2.113, 23.888}; + std::vector v = {'[', '#', 'i', 5, + 'D', 0xb8, 0x1e, 0x85, 0xeb, 0x51, 0xf8, 0x3d, 0x40, + 'D', 0xe1, 0x7a, 0x14, 0xae, 0x47, 0x21, 0x3f, 0x40, + 'D', 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x50, 0x40, + 'D', 0x81, 0x95, 0x43, 0x8b, 0x6c, 0xe7, 0x00, 0x40, + 'D', 0x17, 0xd9, 0xce, 0xf7, 0x53, 0xe3, 0x37, 0x40, + }; + CHECK(json::to_bjdata(j, true) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Optimized with type & count") + { + // note the floats have been replaced by doubles + json j = {29.97, 31.13, 67.0, 2.113, 23.888}; + std::vector v = {'[', '$', 'D', '#', 'i', 5, + 0xb8, 0x1e, 0x85, 0xeb, 0x51, 0xf8, 0x3d, 0x40, + 0xe1, 0x7a, 0x14, 0xae, 0x47, 0x21, 0x3f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x50, 0x40, + 0x81, 0x95, 0x43, 0x8b, 0x6c, 0xe7, 0x00, 0x40, + 0x17, 0xd9, 0xce, 0xf7, 0x53, 0xe3, 0x37, 0x40, + }; + CHECK(json::to_bjdata(j, true, true) == v); + CHECK(json::from_bjdata(v) == j); + } + } + + SECTION("Object Example") + { + SECTION("No Optimization") + { + // note the floats have been replaced by doubles + json j = { {"lat", 29.976}, {"long", 31.131}, {"alt", 67.0} }; + std::vector v = {'{', + 'i', 3, 'a', 'l', 't', 'D', 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x50, 0x40, + 'i', 3, 'l', 'a', 't', 'D', 0x60, 0xe5, 0xd0, 0x22, 0xdb, 0xf9, 0x3d, 0x40, + 'i', 4, 'l', 'o', 'n', 'g', 'D', 0xa8, 0xc6, 0x4b, 0x37, 0x89, 0x21, 0x3f, 0x40, + '}' + }; + CHECK(json::to_bjdata(j) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Optimized with count") + { + // note the floats have been replaced by doubles + json j = { {"lat", 29.976}, {"long", 31.131}, {"alt", 67.0} }; + std::vector v = {'{', '#', 'i', 3, + 'i', 3, 'a', 'l', 't', 'D', 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x50, 0x40, + 'i', 3, 'l', 'a', 't', 'D', 0x60, 0xe5, 0xd0, 0x22, 0xdb, 0xf9, 0x3d, 0x40, + 'i', 4, 'l', 'o', 'n', 'g', 'D', 0xa8, 0xc6, 0x4b, 0x37, 0x89, 0x21, 0x3f, 0x40, + }; + CHECK(json::to_bjdata(j, true) == v); + CHECK(json::from_bjdata(v) == j); + } + + SECTION("Optimized with type & count") + { + // note the floats have been replaced by doubles + json j = { {"lat", 29.976}, {"long", 31.131}, {"alt", 67.0} }; + std::vector v = {'{', '$', 'D', '#', 'i', 3, + 'i', 3, 'a', 'l', 't', 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x50, 0x40, + 'i', 3, 'l', 'a', 't', 0x60, 0xe5, 0xd0, 0x22, 0xdb, 0xf9, 0x3d, 0x40, + 'i', 4, 'l', 'o', 'n', 'g', 0xa8, 0xc6, 0x4b, 0x37, 0x89, 0x21, 0x3f, 0x40, + }; + CHECK(json::to_bjdata(j, true, true) == v); + CHECK(json::from_bjdata(v) == j); + } + } + + SECTION("Special Cases (Null, No-Op and Boolean)") + { + SECTION("Array") + { + std::vector v = {'[', '$', 'N', '#', 'I', 0x00, 0x02}; + CHECK(json::from_bjdata(v, true, true).is_discarded()); + } + + SECTION("Object") + { + std::vector v = {'{', '$', 'Z', '#', 'i', 3, 'i', 4, 'n', 'a', 'm', 'e', 'i', 8, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd', 'i', 5, 'e', 'm', 'a', 'i', 'l'}; + CHECK(json::from_bjdata(v, true, true).is_discarded()); + } + } + } +} + +#if !defined(JSON_NOEXCEPTION) +TEST_CASE("all BJData first bytes") +{ + // these bytes will fail immediately with exception parse_error.112 + std::set supported = + { + 'T', 'F', 'Z', 'U', 'i', 'I', 'l', 'L', 'd', 'D', 'C', 'S', '[', '{', 'N', 'H', 'u', 'm', 'M', 'h' + }; + + for (auto i = 0; i < 256; ++i) + { + const auto byte = static_cast(i); + CAPTURE(byte) + + try + { + auto res = json::from_bjdata(std::vector(1, byte)); + } + catch (const json::parse_error& e) + { + // check that parse_error.112 is only thrown if the + // first byte is not in the supported set + INFO_WITH_TEMP(e.what()); + if (supported.find(byte) == supported.end()) + { + CHECK(e.id == 112); + } + else + { + CHECK(e.id != 112); + } + } + } +} +#endif + +TEST_CASE("BJData roundtrips" * doctest::skip()) +{ + SECTION("input from self-generated BJData files") + { + for (std::string filename : + { + TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json", + TEST_DATA_DIRECTORY "/json.org/1.json", + TEST_DATA_DIRECTORY "/json.org/2.json", + TEST_DATA_DIRECTORY "/json.org/3.json", + TEST_DATA_DIRECTORY "/json.org/4.json", + TEST_DATA_DIRECTORY "/json.org/5.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip01.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip02.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip03.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip04.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip05.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip06.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip07.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip08.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip09.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip10.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip11.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip12.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip13.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip14.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip15.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip16.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip17.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip18.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip19.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip20.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip21.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip22.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip23.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip24.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip25.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip26.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip27.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip28.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip29.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip30.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip31.json", + TEST_DATA_DIRECTORY "/json_roundtrip/roundtrip32.json", + TEST_DATA_DIRECTORY "/json_testsuite/sample.json", + TEST_DATA_DIRECTORY "/json_tests/pass1.json", + TEST_DATA_DIRECTORY "/json_tests/pass2.json", + TEST_DATA_DIRECTORY "/json_tests/pass3.json" + }) + { + CAPTURE(filename) + + { + INFO_WITH_TEMP(filename + ": std::vector"); + // parse JSON file + std::ifstream f_json(filename); + json j1 = json::parse(f_json); + + // parse BJData file + auto packed = utils::read_binary_file(filename + ".bjdata"); + json j2; + CHECK_NOTHROW(j2 = json::from_bjdata(packed)); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + { + INFO_WITH_TEMP(filename + ": std::ifstream"); + // parse JSON file + std::ifstream f_json(filename); + json j1 = json::parse(f_json); + + // parse BJData file + std::ifstream f_bjdata(filename + ".bjdata", std::ios::binary); + json j2; + CHECK_NOTHROW(j2 = json::from_bjdata(f_bjdata)); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + { + INFO_WITH_TEMP(filename + ": uint8_t* and size"); + // parse JSON file + std::ifstream f_json(filename); + json j1 = json::parse(f_json); + + // parse BJData file + auto packed = utils::read_binary_file(filename + ".bjdata"); + json j2; + CHECK_NOTHROW(j2 = json::from_bjdata({packed.data(), packed.size()})); + + // compare parsed JSON values + CHECK(j1 == j2); + } + + { + INFO_WITH_TEMP(filename + ": output to output adapters"); + // parse JSON file + std::ifstream f_json(filename); + json j1 = json::parse(f_json); + + // parse BJData file + auto packed = utils::read_binary_file(filename + ".bjdata"); + + { + INFO_WITH_TEMP(filename + ": output adapters: std::vector"); + std::vector vec; + json::to_bjdata(j1, vec); + CHECK(vec == packed); + } + } + } + } +} From 5352856f04730204123285ed928fc720ee8348b3 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 29 Apr 2022 21:40:02 +0200 Subject: [PATCH 049/110] Implement support for string_view (attempt no. 3) (#3423) * Add key_compare member to ordered_map * Replace == with key_compare in ordered_map * Expose the actual comparison function used by object_t nlohmann::ordered_map uses a different comparison function than the one provided via template parameter. * Introduce a type trait to detect if object_t has a key_compare member. * Rename object_comparator_t to default_object_comparator_t. * Add object_comparator_t to be conditionally defined as object_t::key_compare, if available, or default_object_comparator_t otherwise. * Update the documentation accordingly. Co-authored-by: Niels Lohmann * Add type traits to check if a type is usable as object key Add type trait to check: * if a type is a specialization of a template. * if a type is a json_pointer. * if a type is a basic_json::{const_,}iterator. * if two types are comparable using a given comparison functor. * if a type is comparable to basic_json::object_t::key_type. * if a type has a member type is_transparent. * if a type is usable as object key. * if a type has an erase() function accepting a given KeyType. Co-authored-by: Niels Lohmann * Rework basic_json element access to accept more key types Rework basic_json element access member functions and operators to accept any type that meets the requirements defined by type trait detail::is_usable_as_key_type. Member functions and operators: * at() * operator[] * value() * erase() * find() * count() * contains() Update documentation to reflect these changes. Add unit tests to excercise the new functions using std::string_view. Co-authored-by: Niels Lohmann Co-authored-by: Niels Lohmann --- doc/mkdocs/docs/api/basic_json/at.md | 41 +- doc/mkdocs/docs/api/basic_json/basic_json.md | 10 +- doc/mkdocs/docs/api/basic_json/contains.md | 32 +- doc/mkdocs/docs/api/basic_json/count.md | 23 +- .../basic_json/default_object_comparator_t.md | 19 + doc/mkdocs/docs/api/basic_json/erase.md | 39 +- doc/mkdocs/docs/api/basic_json/find.md | 31 +- doc/mkdocs/docs/api/basic_json/index.md | 1 + doc/mkdocs/docs/api/basic_json/insert.md | 2 +- .../api/basic_json/object_comparator_t.md | 16 +- doc/mkdocs/docs/api/basic_json/object_t.md | 4 +- doc/mkdocs/docs/api/basic_json/operator[].md | 52 +- doc/mkdocs/docs/api/basic_json/to_bson.md | 2 +- doc/mkdocs/docs/api/basic_json/to_cbor.md | 2 +- doc/mkdocs/docs/api/basic_json/to_msgpack.md | 2 +- doc/mkdocs/docs/api/basic_json/to_ubjson.md | 2 +- doc/mkdocs/docs/api/basic_json/value.md | 32 +- doc/mkdocs/docs/api/ordered_map.md | 7 + doc/mkdocs/mkdocs.yml | 1 + include/nlohmann/detail/macro_scope.hpp | 6 + include/nlohmann/detail/macro_unscope.hpp | 1 + include/nlohmann/detail/meta/type_traits.hpp | 95 +++- include/nlohmann/json.hpp | 370 +++++++++---- include/nlohmann/ordered_map.hpp | 26 +- single_include/nlohmann/json.hpp | 498 +++++++++++++---- test/src/unit-element_access2.cpp | 508 ++++++++++++++++++ 26 files changed, 1517 insertions(+), 305 deletions(-) create mode 100644 doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md diff --git a/doc/mkdocs/docs/api/basic_json/at.md b/doc/mkdocs/docs/api/basic_json/at.md index fc518f46e..dbd4cb6c9 100644 --- a/doc/mkdocs/docs/api/basic_json/at.md +++ b/doc/mkdocs/docs/api/basic_json/at.md @@ -10,13 +10,28 @@ reference at(const typename object_t::key_type& key); const_reference at(const typename object_t::key_type& key) const; // (3) +template +reference at(KeyType&& key); +template +const_reference at(KeyType&& key) const; + +// (4) reference at(const json_pointer& ptr); const_reference at(const json_pointer& ptr) const; ``` 1. Returns a reference to the array element at specified location `idx`, with bounds checking. -2. Returns a reference to the object element at with specified key `key`, with bounds checking. -3. Returns a reference to the element at with specified JSON pointer `ptr`, with bounds checking. +2. Returns a reference to the object element with specified key `key`, with bounds checking. +3. See 2. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. +4. Returns a reference to the element at specified JSON pointer `ptr`, with bounds checking. + +## Template parameters + +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). ## Parameters @@ -25,15 +40,16 @@ const_reference at(const json_pointer& ptr) const; `key` (in) : object key of the elements to access - + `ptr` (in) : JSON pointer to the desired element - + ## Return value 1. reference to the element at index `idx` 2. reference to the element at key `key` -3. reference to the element pointed to by `ptr` +3. reference to the element at key `key` +4. reference to the element pointed to by `ptr` ## Exception safety @@ -51,7 +67,8 @@ Strong exception safety: if an exception occurs, the original value stays intact in this case, calling `at` with a key makes no sense. See example below. - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if the key `key` is not stored in the object; that is, `find(key) == end()`. See example below. -3. The function can throw the following exceptions: +3. See 2. +4. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed JSON pointer `ptr` begins with '0'. See example below. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed @@ -68,9 +85,10 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Complexity -1. Constant +1. Constant. 2. Logarithmic in the size of the container. -3. Constant +3. Logarithmic in the size of the container. +4. Logarithmic in the size of the container. ## Examples @@ -134,7 +152,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/at__object_t_key_type_const.output" ``` -??? example "Example (3) access specified element via JSON Pointer" +??? example "Example (4) access specified element via JSON Pointer" The example below shows how object elements can be read and written using `at()`. It also demonstrates the different exceptions that can be thrown. @@ -149,7 +167,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/at_json_pointer.output" ``` -??? example "Example (3) access specified element via JSON Pointer" +??? example "Example (4) access specified element via JSON Pointer" The example below shows how object elements can be read using `at()`. It also demonstrates the different exceptions that can be thrown. @@ -173,4 +191,5 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Added in version 1.0.0. 2. Added in version 1.0.0. -3. Added in version 2.0.0. +3. Added in version 3.11.0. +4. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/basic_json.md b/doc/mkdocs/docs/api/basic_json/basic_json.md index afa3901d1..9a289d6e9 100644 --- a/doc/mkdocs/docs/api/basic_json/basic_json.md +++ b/doc/mkdocs/docs/api/basic_json/basic_json.md @@ -201,16 +201,16 @@ basic_json(basic_json&& other) noexcept; ## Exceptions -1. / +1. (none) 2. The function does not throw exceptions. -3. / -4. / +3. (none) +4. (none) 5. The function can throw the following exceptions: - Throws [`type_error.301`](../../home/exceptions.md#jsonexceptiontype_error301) if `type_deduction` is `#!cpp false`, `manual_type` is `value_t::object`, but `init` contains an element which is not a pair whose first element is a string. In this case, the constructor could not create an object. If `type_deduction` would have been `#!cpp true`, an array would have been created. See `object(initializer_list_t)` for an example. -6. / +6. (none) 7. The function can throw the following exceptions: - Throws [`invalid_iterator.201`](../../home/exceptions.md#jsonexceptioninvalid_iterator201) if iterators `first` and `last` are not compatible (i.e., do not belong to the same JSON value). In this case, the range @@ -220,7 +220,7 @@ basic_json(basic_json&& other) noexcept; element anymore. In this case, the range `[first, last)` is undefined. See example code below. - Throws [`invalid_iterator.206`](../../home/exceptions.md#jsonexceptioninvalid_iterator206) if iterators `first` and `last` belong to a `#!json null` value. In this case, the range `[first, last)` is undefined. -8. / +8. (none) 9. The function does not throw exceptions. ## Complexity diff --git a/doc/mkdocs/docs/api/basic_json/contains.md b/doc/mkdocs/docs/api/basic_json/contains.md index 8463f4ea9..67a5ffcc0 100644 --- a/doc/mkdocs/docs/api/basic_json/contains.md +++ b/doc/mkdocs/docs/api/basic_json/contains.md @@ -2,21 +2,28 @@ ```cpp // (1) -template -bool contains(KeyT && key) const; +bool contains(const typename object_t::key_type& key) const; // (2) +template +bool contains(KeyType&& key) const; + +// (3) bool contains(const json_pointer& ptr) const; ``` -1. Check whether an element exists in a JSON object with key equivalent to `key`. If the element is not found or the +1. Check whether an element exists in a JSON object with a key equivalent to `key`. If the element is not found or the JSON value is not an object, `#!cpp false` is returned. -2. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value. +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. +3. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value. ## Template parameters -`KeyT` -: A type for an object key other than `basic_json::json_pointer`. +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). ## Parameters @@ -30,7 +37,8 @@ bool contains(const json_pointer& ptr) const; 1. `#!cpp true` if an element with specified `key` exists. If no such element with such key is found or the JSON value is not an object, `#!cpp false` is returned. -2. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. +2. See 1. +3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. ## Exception safety @@ -39,7 +47,8 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Exceptions 1. The function does not throw exceptions. -2. The function can throw the following exceptions: +2. The function does not throw exceptions. +3. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index begins with `0`. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index was not a @@ -74,7 +83,7 @@ Logarithmic in the size of the JSON object. --8<-- "examples/contains.output" ``` -??? example "Example (1) check with JSON pointer" +??? example "Example (3) check with JSON pointer" The example shows how `contains()` is used. @@ -90,5 +99,6 @@ Logarithmic in the size of the JSON object. ## Version history -1. Added in version 3.6.0. -2. Added in version 3.7.0. +1. Added in version 3.11.0. +2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0. +3. Added in version 3.7.0. diff --git a/doc/mkdocs/docs/api/basic_json/count.md b/doc/mkdocs/docs/api/basic_json/count.md index fcfef8673..de135be21 100644 --- a/doc/mkdocs/docs/api/basic_json/count.md +++ b/doc/mkdocs/docs/api/basic_json/count.md @@ -1,17 +1,25 @@ # nlohmann::basic_json::count ```cpp -template -size_type count(KeyT&& key) const; +// (1) +size_type count(const typename object_t::key_type& key) const; + +// (2) +template +size_type count(KeyType&& key) const; ``` -Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will -always be `0` (`key` was not found) or `1` (`key` was found). +1. Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will + always be `0` (`key` was not found) or `1` (`key` was found). +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. ## Template parameters -`KeyT` -: A type for an object key. +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). ## Parameters @@ -52,4 +60,5 @@ This method always returns `0` when executed on a JSON type that is not an objec ## Version history -- Added in version 1.0.0. +1. Added in version 3.11.0. +2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md b/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md new file mode 100644 index 000000000..9e5f6c5bd --- /dev/null +++ b/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md @@ -0,0 +1,19 @@ +# nlohmann::basic_json::default_object_comparator_t + +```cpp +using default_object_comparator_t = std::less; // until C++14 + +using default_object_comparator_t = std::less<>; // since C++14 +``` + +The default comparator used by [`object_t`](object_t.md). + +Since C++14 a transparent comparator is used which prevents unnecessary string construction +when looking up a key in an object. + +The actual comparator used depends on [`object_t`](object_t.md) and can be obtained via +[`object_comparator_t`](object_comparator_t.md). + +## Version history + +- Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/erase.md b/doc/mkdocs/docs/api/basic_json/erase.md index d94c25b7f..6cb749b8c 100644 --- a/doc/mkdocs/docs/api/basic_json/erase.md +++ b/doc/mkdocs/docs/api/basic_json/erase.md @@ -13,6 +13,10 @@ const_iterator erase(const_iterator first, const_iterator last); size_type erase(const typename object_t::key_type& key); // (4) +template +size_type erase(KeyType&& key); + +// (5) void erase(const size_type idx); ``` @@ -29,7 +33,17 @@ void erase(const size_type idx); 3. Removes an element from a JSON object by key. -4. Removes an element from a JSON array by index. +4. See 3. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. + +5. Removes an element from a JSON array by index. + +## Template parameters + +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). ## Parameters @@ -56,7 +70,8 @@ void erase(const size_type idx); is returned. 3. Number of elements removed. If `ObjectType` is the default `std::map` type, the return value will always be `0` (`key` was not found) or `1` (`key` was found). -4. / +4. See 3. +5. (none) ## Exception safety @@ -83,7 +98,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 3. The function can throw the following exceptions: - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than JSON object; example: `"cannot use erase() with null"` -4. The function can throw the following exceptions: +4. See 3. +5. The function can throw the following exceptions: - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than JSON object; example: `"cannot use erase() with null"` - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) when `idx >= size()`; example: @@ -103,14 +119,16 @@ Strong exception safety: if an exception occurs, the original value stays intact - strings and binary: linear in the length of the member - other types: constant 3. `log(size()) + count(key)` -4. Linear in distance between `idx` and the end of the container. +4. `log(size()) + count(key)` +5. Linear in distance between `idx` and the end of the container. ## Notes 1. Invalidates iterators and references at or after the point of the `erase`, including the `end()` iterator. -2. / +2. (none) 3. References and iterators to the erased elements are invalidated. Other references and iterators are not affected. -4. / +4. See 3. +5. (none) ## Examples @@ -156,7 +174,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/erase__key_type.output" ``` -??? example "Example: (4) remove element from a JSON array given an index" +??? example "Example: (5) remove element from a JSON array given an index" The example shows the effect of `erase()` using an array index. @@ -172,5 +190,8 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history -- Added in version 1.0.0. -- Added support for binary types in version 3.8.0. +1. Added in version 1.0.0. Added support for binary types in version 3.8.0. +2. Added in version 1.0.0. Added support for binary types in version 3.8.0. +3. Added in version 1.0.0. +4. Added in version 3.11.0. +5. Added in version 1.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/find.md b/doc/mkdocs/docs/api/basic_json/find.md index af4cb2972..d4fddc579 100644 --- a/doc/mkdocs/docs/api/basic_json/find.md +++ b/doc/mkdocs/docs/api/basic_json/find.md @@ -1,20 +1,28 @@ # nlohmann::basic_json::find ```cpp -template -iterator find(KeyT&& key); +// (1) +iterator find(const typename object_t::key_type& key); +const_iterator find(const typename object_t::key_type& key) const; -template -const_iterator find(KeyT&& key) const; +// (2) +template +iterator find(KeyType&& key); +template +const_iterator find(KeyType&& key) const; ``` -Finds an element in a JSON object with key equivalent to `key`. If the element is not found or the JSON value is not an -object, `end()` is returned. +1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the + JSON value is not an object, `end()` is returned. +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. ## Template parameters -`KeyT` -: A type for an object key. +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). ## Parameters @@ -23,8 +31,8 @@ object, `end()` is returned. ## Return value -Iterator to an element with key equivalent to `key`. If no such element is found or the JSON value is not an object, -past-the-end (see `end()`) iterator is returned. +Iterator to an element with a key equivalent to `key`. If no such element is found or the JSON value is not an object, +a past-the-end iterator (see `end()`) is returned. ## Exception safety @@ -60,4 +68,5 @@ This method always returns `end()` when executed on a JSON type that is not an o ## Version history -- Added in version 1.0.0. +1. Added in version 3.11.0. +2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/index.md b/doc/mkdocs/docs/api/basic_json/index.md index 286fea2b4..68ac063ff 100644 --- a/doc/mkdocs/docs/api/basic_json/index.md +++ b/doc/mkdocs/docs/api/basic_json/index.md @@ -128,6 +128,7 @@ The class satisfies the following concept requirements: - [**array_t**](array_t.md) - type for arrays - [**binary_t**](binary_t.md) - type for binary arrays - [**boolean_t**](boolean_t.md) - type for booleans +- [**default_object_comparator_t**](default_object_comparator_t.md) - default comparator for objects - [**number_float_t**](number_float_t.md) - type for numbers (floating-point) - [**number_integer_t**](number_integer_t.md) - type for numbers (integer) - [**number_unsigned_t**](number_unsigned_t.md) - type for numbers (unsigned) diff --git a/doc/mkdocs/docs/api/basic_json/insert.md b/doc/mkdocs/docs/api/basic_json/insert.md index fdd8fe6b5..2e6b29301 100644 --- a/doc/mkdocs/docs/api/basic_json/insert.md +++ b/doc/mkdocs/docs/api/basic_json/insert.md @@ -50,7 +50,7 @@ void insert(const_iterator first, const_iterator last); 2. iterator pointing to the first element inserted, or `pos` if `#!cpp cnt==0` 3. iterator pointing to the first element inserted, or `pos` if `#!cpp first==last` 4. iterator pointing to the first element inserted, or `pos` if `ilist` is empty -5. / +5. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/object_comparator_t.md b/doc/mkdocs/docs/api/basic_json/object_comparator_t.md index e2bc79d05..6c64b6453 100644 --- a/doc/mkdocs/docs/api/basic_json/object_comparator_t.md +++ b/doc/mkdocs/docs/api/basic_json/object_comparator_t.md @@ -1,16 +1,16 @@ # nlohmann::basic_json::object_comparator_t -```cpp -using object_comparator_t = std::less; // until C++14 -using object_comparator_t = std::less<>; // since C++14 +```cpp +using object_comparator_t = typename object_t::key_compare; +// or +using object_comparator_t = default_object_comparator_t; ``` -The comparator used in [`object_t`](object_t.md). - -When C++14 is detected, a transparent comparator is used which, when combined with perfect forwarding on find() and -count() calls, prevents unnecessary string construction. +The comparator used by [`object_t`](object_t.md). Defined as `#!cpp typename object_t::key_compare` if available, +and [`default_object_comparator_t`](default_object_comparator_t.md) otherwise. ## Version history -- Unknown. +- Added in version 3.0.0. +- Changed to be conditionally defined as `#!cpp typename object_t::key_compare` or `default_object_comparator_t` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/object_t.md b/doc/mkdocs/docs/api/basic_json/object_t.md index d4bea15aa..67b3bb78c 100644 --- a/doc/mkdocs/docs/api/basic_json/object_t.md +++ b/doc/mkdocs/docs/api/basic_json/object_t.md @@ -3,7 +3,7 @@ ```cpp using object_t = ObjectType>>; ``` @@ -52,7 +52,7 @@ std::map< > ``` -See [`object_comparator_t`](object_comparator_t.md) for more information. +See [`default_object_comparator_t`](default_object_comparator_t.md) for more information. #### Behavior diff --git a/doc/mkdocs/docs/api/basic_json/operator[].md b/doc/mkdocs/docs/api/basic_json/operator[].md index cc9eae7f3..cd5638b97 100644 --- a/doc/mkdocs/docs/api/basic_json/operator[].md +++ b/doc/mkdocs/docs/api/basic_json/operator[].md @@ -6,26 +6,32 @@ reference operator[](size_type idx); const_reference operator[](size_type idx) const; // (2) -reference operator[](const typename object_t::key_type& key); +reference operator[](typename object_t::key_type key); const_reference operator[](const typename object_t::key_type& key) const; -template -reference operator[](T* key); -template -const_reference operator[](T* key) const; // (3) +template +reference operator[](KeyType&& key); +template +const_reference operator[](KeyType&& key) const; + +// (4) reference operator[](const json_pointer& ptr); const_reference operator[](const json_pointer& ptr) const; ``` 1. Returns a reference to the array element at specified location `idx`. -2. Returns a reference to the object element at with specified key `key`. -3. Returns a reference to the element at with specified JSON pointer `ptr`. +2. Returns a reference to the object element with specified key `key`. The non-const qualified overload takes the key by value. +3. See 2. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. +4. Returns a reference to the element with specified JSON pointer `ptr`. ## Template parameters -`T` -: string literal convertible to `object_t::key_type` +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). ## Parameters @@ -40,9 +46,10 @@ const_reference operator[](const json_pointer& ptr) const; ## Return value -1. reference to the element at index `idx` -2. reference to the element at key `key` -3. reference to the element pointed to by `ptr` +1. (const) reference to the element at index `idx` +2. (const) reference to the element at key `key` +3. (const) reference to the element at key `key` +4. (const) reference to the element pointed to by `ptr` ## Exception safety @@ -56,7 +63,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 2. The function can throw the following exceptions: - Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an object or null; in that case, using the `[]` operator with a key makes no sense. -3. The function can throw the following exceptions: +3. See 2. +4. The function can throw the following exceptions: - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed JSON pointer `ptr` begins with '0'. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed @@ -70,7 +78,8 @@ Strong exception safety: if an exception occurs, the original value stays intact 1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`. 2. Logarithmic in the size of the container. -3. Constant +3. Logarithmic in the size of the container. +4. Logarithmic in the size of the container. ## Notes @@ -87,7 +96,9 @@ Strong exception safety: if an exception occurs, the original value stays intact 2. If `key` is not found in the object, then it is silently added to the object and filled with a `#!json null` value to make `key` a valid reference. In case the value was `#!json null` before, it is converted to an object. -3. `null` values are created in arrays and objects if necessary. +3. See 2. + +4. `null` values are created in arrays and objects if necessary. In particular: @@ -143,7 +154,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/operatorarray__key_type.output" ``` -??? example "Example (2): access specified object element" +??? example "Example (2): access specified object element (const)" The example below shows how object elements can be read using the `[]` operator. @@ -157,7 +168,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/operatorarray__key_type_const.output" ``` -??? example "Example (3): access specified element via JSON Pointer" +??? example "Example (4): access specified element via JSON Pointer" The example below shows how values can be read and written using JSON Pointers. @@ -171,7 +182,7 @@ Strong exception safety: if an exception occurs, the original value stays intact --8<-- "examples/operatorjson_pointer.output" ``` -??? example "Example (3): access specified element via JSON Pointer" +??? example "Example (4): access specified element via JSON Pointer (const)" The example below shows how values can be read using JSON Pointers. @@ -193,5 +204,6 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history 1. Added in version 1.0.0. -2. Added in version 1.0.0. Overloads for `T* key` added in version 1.1.0. -3. Added in version 2.0.0. +2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3) in version 3.11.0. +3. Added in version 3.11.0. +4. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/to_bson.md b/doc/mkdocs/docs/api/basic_json/to_bson.md index 664dd0e20..5c4324a3f 100644 --- a/doc/mkdocs/docs/api/basic_json/to_bson.md +++ b/doc/mkdocs/docs/api/basic_json/to_bson.md @@ -28,7 +28,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. BSON serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/to_cbor.md b/doc/mkdocs/docs/api/basic_json/to_cbor.md index 05d85ed85..0f944c481 100644 --- a/doc/mkdocs/docs/api/basic_json/to_cbor.md +++ b/doc/mkdocs/docs/api/basic_json/to_cbor.md @@ -29,7 +29,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. CBOR serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/to_msgpack.md b/doc/mkdocs/docs/api/basic_json/to_msgpack.md index fb4b40bd0..7d40981d5 100644 --- a/doc/mkdocs/docs/api/basic_json/to_msgpack.md +++ b/doc/mkdocs/docs/api/basic_json/to_msgpack.md @@ -28,7 +28,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. MessagePack serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/to_ubjson.md b/doc/mkdocs/docs/api/basic_json/to_ubjson.md index 0a3d87e54..e3cd5d62b 100644 --- a/doc/mkdocs/docs/api/basic_json/to_ubjson.md +++ b/doc/mkdocs/docs/api/basic_json/to_ubjson.md @@ -39,7 +39,7 @@ The exact mapping and its limitations is described on a [dedicated page](../../f ## Return value 1. UBJSON serialization as byte vector -2. / +2. (none) ## Exception safety diff --git a/doc/mkdocs/docs/api/basic_json/value.md b/doc/mkdocs/docs/api/basic_json/value.md index 1844c41fb..6a1f3481d 100644 --- a/doc/mkdocs/docs/api/basic_json/value.md +++ b/doc/mkdocs/docs/api/basic_json/value.md @@ -4,9 +4,14 @@ // (1) template ValueType value(const typename object_t::key_type& key, - const ValueType& default_value) const; + ValueType&& default_value) const; // (2) +template +ValueType value(KeyType&& key, + ValueType&& default_value) const; + +// (3) template ValueType value(const json_pointer& ptr, const ValueType& default_value) const; @@ -24,7 +29,10 @@ ValueType value(const json_pointer& ptr, } ``` -2. Returns either a copy of an object's element at the specified JSON pointer `ptr` or a given default value if no value +2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and + `#!cpp typename object_comparator_t::is_transparent` denotes a type. + +3. Returns either a copy of an object's element at the specified JSON pointer `ptr` or a given default value if no value at `ptr` exists. The function is basically equivalent to executing @@ -44,6 +52,10 @@ ValueType value(const json_pointer& ptr, ## Template parameters +`KeyType` +: A type for an object key other than [`json_pointer`](../json_pointer/index.md) that is comparable with + [`string_t`](string_t.md) using [`object_comparator_t`](object_comparator_t.md). + This can also be a string view (C++17). `ValueType` : type compatible to JSON values, for instance `#!cpp int` for JSON integer numbers, `#!cpp bool` for JSON booleans, or `#!cpp std::vector` types for JSON arrays. Note the type of the expected value at `key`/`ptr` and the default @@ -55,7 +67,7 @@ ValueType value(const json_pointer& ptr, : key of the element to access `default_value` (in) -: the value to return if key/ptr found no value +: the value to return if `key`/`ptr` found no value `ptr` (in) : a JSON pointer to the element to access @@ -63,7 +75,8 @@ ValueType value(const json_pointer& ptr, ## Return value 1. copy of the element at key `key` or `default_value` if `key` is not found -1. copy of the element at JSON Pointer `ptr` or `default_value` if no value for `ptr` is found +2. copy of the element at key `key` or `default_value` if `key` is not found +3. copy of the element at JSON Pointer `ptr` or `default_value` if no value for `ptr` is found ## Exception safety @@ -77,7 +90,8 @@ changes to any JSON value. the type of the value at `key` - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object; in that case, using `value()` with a key makes no sense. -2. The function can throw the following exceptions: +2. See 1. +3. The function can throw the following exceptions: - Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match the type of the value at `ptr` - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object; @@ -87,6 +101,7 @@ changes to any JSON value. 1. Logarithmic in the size of the container. 2. Logarithmic in the size of the container. +3. Logarithmic in the size of the container. ## Examples @@ -104,7 +119,7 @@ changes to any JSON value. --8<-- "examples/basic_json__value.output" ``` -??? example "Example (2): access specified object element via JSON Pointer with default value" +??? example "Example (3): access specified object element via JSON Pointer with default value" The example below shows how object elements can be queried with a default value. @@ -125,5 +140,6 @@ changes to any JSON value. ## Version history -1. Added in version 1.0.0. -2. Added in version 2.0.2. +1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0. +2. Added in version 3.11.0. +3. Added in version 2.0.2. diff --git a/doc/mkdocs/docs/api/ordered_map.md b/doc/mkdocs/docs/api/ordered_map.md index 74b248ff2..160b85c28 100644 --- a/doc/mkdocs/docs/api/ordered_map.md +++ b/doc/mkdocs/docs/api/ordered_map.md @@ -32,6 +32,12 @@ A minimal map-like container that preserves insertion order for use within [`nlo - **const_iterator** - **size_type** - **value_type** +- **key_compare** - key comparison function +```cpp +std::equal_to // until C++14 + +std::equal_to<> // since C++14 +``` ## Member functions @@ -68,3 +74,4 @@ A minimal map-like container that preserves insertion order for use within [`nlo ## Version history - Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md). +- Added **key_compare** member in version 3.11.0. diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 59f0ae700..302e827cf 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -97,6 +97,7 @@ nav: - 'count': api/basic_json/count.md - 'crbegin': api/basic_json/crbegin.md - 'crend': api/basic_json/crend.md + - 'default_object_comparator_t': api/basic_json/default_object_comparator_t.md - 'diff': api/basic_json/diff.md - 'dump': api/basic_json/dump.md - 'emplace': api/basic_json/emplace.md diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index f636b908a..cc9ac5fc7 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -106,6 +106,12 @@ #endif #endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index 377d3f11d..ec57b02cc 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -14,6 +14,7 @@ #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL +#undef JSON_NO_UNIQUE_ADDRESS #ifndef JSON_TEST_KEEP_MACROS #undef JSON_CATCH diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 376c00a09..2cc13f3ac 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -54,11 +54,6 @@ struct is_basic_json_context : || std::is_same::value > {}; -template struct is_json_pointer : std::false_type {}; - -template -struct is_json_pointer> : std::true_type {}; - ////////////////////// // json_ref helpers // ////////////////////// @@ -160,6 +155,24 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> T>::value; }; +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; /////////////////// // is_ functions // @@ -454,6 +467,78 @@ struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template