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")