Files
json/include/nlohmann/detail/exceptions.hpp
T
Niels Lohmann fe5e401870 Consolidate duplicated detail helpers and bodies (#5891) (#5901)
* Add shared detail helpers for the #5891 cleanup

Add detail::is_primitive(value_t), detail::hex4_lower(), detail::utf8_shape(),
detail::input_format_name(), detail::empty_input_message(), the exception ids
116, 319, 320 and 416 to detail::exception_id, and make
detail::is_ascii_copyable() constexpr. Later commits switch the duplicated
code over to them.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.4: BSON writers share one write-pass UTF-8 sanitizer (#5891)

write_bson_entry_header and write_bson_string re-derived the error-handler
policy differently. Both now use sanitize_utf8_for_bson_write, which treats
strict like keep (the size pass already validated) and sanitizes replace/ignore
into the bytes the size pass measured. No context plumbing is needed.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.1.1: one MessagePack container prefix helper (#5891)

Array and object prefixes differed only in the fix marker and the 16/32-bit
markers; both now delegate to write_msgpack_container_prefix. Output is
byte-identical.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.1.2: one iterative walker for CBOR and MessagePack (#5891)

write_cbor_iterative and write_msgpack_iterative (and their *_value_or_push
helpers) were identical but for the head, scalar, and key writers. They are now
one template over a small policy struct, so everything inlines. BON8 keeps its
own walker (open string run, closing marker), noted in a comment.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.1.3, 2.1.8: one UBJSON container start helper, shared frame base (#5891)

write_ubjson_start_array/_object are now write_ubjson_start_container<IsArray>,
keeping the array-only begin()+1 scan and excessive_valueless rule.
ubjson_frame derives from binary_container_frame instead of duplicating it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.1.4: write_bytes helper for the byte-string write sites (#5891)

The 13 oa.write_characters(reinterpret_cast<const CharType*>(x.data()), n)
sites now call write_bytes(const void*, size_t); copy_characters sites and the
buffer-based writes are untouched.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.1.5: shared object-key writer for CBOR and MessagePack (#5891)

write_cbor_key and write_msgpack_key were the same SFINAE pair apart from the
string/scalar writers; both now delegate to write_object_key<Walk>. UBJSON key
and to_format_length stay as they are.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.10: writer-side format names from input_format_name (#5891)

The CBOR/MessagePack/UBJSON/BSON/BJData/BON8 literals in throw_on_discarded and
throw_integer_out_of_range calls now come from detail::input_format_name; the
texts are identical, and UBJSON vs BJData still follows use_bjdata.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* serializer: constexpr ascii table, hex4_lower, dedupe helpers (#5891)

1.1: build ascii_copyable() at compile time from is_ascii_copyable and
test find_ascii_copyable_run against it for every byte. 1.2/2.5.4: use
detail::hex4_lower in both \uXXXX writers. 2.1.6: factor the repeated
flush/emit blocks in dump_escaped_impl into always-inline helpers.
2.1.7: comment why the escape copy does not use put_buffer.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.6: use string_t-aware to_string/concat in meta() (#5891)

Replaces the four std::to_string sites; rendered text unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.7: use detail::is_primitive in json.hpp switches (#5891)

has_no_children, set_parents, max_size and the two diff walkers.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.1: factor insert()/iterator throw helpers (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.2: share swap() overloads via swap_impl (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.4: factor primitive reset and range check in erase()/range ctor (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.6: share push_back array bodies via push_back_array_impl (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.7: route parse/accept/sax_parse through *_impl helpers (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.8: share the vector-returning to_* bodies via to_binary_vector (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.2.10: merge destroy_string/destroy_binary into destroy_pointer (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.4.1/1.7: exceptions: share what_impl, flatten diagnostics switch (#5891)

The four create() bodies build their message through one protected
static helper; the diagnostics parent walk handles array/object and
ignores all other types.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.4.2/2.4.3/1.7: json_pointer: share slash and escape checks, drop primitive case lists (#5891)

The leading-slash and invalid '~' escape checks are shared helpers; the
reference-token walks test object and array first and treat everything
else as primitive.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.7/2.4.9: iter_impl, iteration_proxy: drop primitive case lists, share compare preamble (#5891)

Object and array stay the first branches; everything else is the
primitive case. operator== and operator< share the different-container
check.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.4.5/2.4.6: from_json: share get_number_value and reserve_if_possible (#5891)

The three number cases of get_arithmetic_value and the arithmetic
from_json share one helper (boolean stays accepted only by the latter);
the array and object reserve overload pairs become one.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.4.8: ordered_map: share erase_impl between the key erase overloads (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.7: json_pointer: flatten walk drops the primitive case list (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.2/1.3/1.5/2.5.1/2.5.2/2.5.5: share helpers in the view code (#5891)

- detail::exception_id at every numeric id; throw_* helpers take it
- hex_byte, hex4_lower, utf8_shape replace local hex/table code
- throw_array_index_out_of_range/_past_end, throw_key_not_found
- has_native_float_format replaces the hand-written IEEE check
No change of messages, ids or output.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.5: use utf8_shape in get_bon8_string, add exhaustive UTF-8 validator test (#5891)

get_bon8_string takes the valid range of the second byte from utf8_shape()
(0xF5..0xF7 stay errors, unget behavior unchanged). validate_one_utf8 keeps its
if-chain (hot bulk path); the new test checks decode, validate_one_utf8,
utf8_shape, json::accept and the BON8 reader against each other for every lead
and second byte.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.1: get_cbor_sized_bytes for the CBOR chunk getters (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.2: get_string_with_length for the plain-order string sites (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.3, 2.3.4, 2.3.10: unsupported_key_type, empty_input_message, input_format_name in the readers (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.7, 2.3.9: read_object_key and leave_container_and_check for CBOR/MessagePack/BON8 (#5891)

UBJSON keys (different getter signature) and its container tail stay as is.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.5: json_sax_dom_error_state base for the DOM SAX parsers (#5891)

Holds errored, allow_exceptions, parse_error() and is_errored(). The scalar
handlers stay in each class (parse hot path).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.6: table of control-character messages in the lexer (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.3.8: finish_input helper for the strict-EOF block in the parser (#5891)

sax_parse keeps its if (result) guard and return value. Also adds the
clang-tidy NOLINT for the 2.3.6 message table.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.5.7: share parse_array_index between json_pointer and json_view (#5891)

Move json_pointer::parse_array_index() and its status enum to namespace detail
(json_pointer keeps a forwarding member), and let the json_view pointer
resolution use it instead of its own copy of the RFC 6901 index grammar. Both
accepted the same tokens with the same outcome. Also update the comment on
detail::decode(), which listed four of the five UTF-8 validators and now points
to the test that keeps them in sync.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Amalgamate (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.4.5: keep the inlined number cases in from_json(ArithmeticType&) (#5891)

Calling get_number_value() from from_json(ArithmeticType&) made
GetArray<std::array<int, 1000>> 2.4x slower (it changes the inlining of the
std::array conversion), so that overload keeps its own switch, with a comment.
get_arithmetic_value() still uses the shared helper.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 2.5.7: force-inline detail::parse_array_index (#5891)

Moving parse_array_index out of json_pointer stopped clang from inlining it
into the pointer resolution (JsonPointerAt/citm_catalog +17%,
JsonPointerAt/twitter +8%); JSON_HEDLEY_ALWAYS_INLINE restores the baseline.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 1.5: keep get_bon8_string's own UTF-8 ranges (#5891)

Taking the second-byte range from utf8_shape() made BON8 decoding 2-3% slower
(FromBinaryShape/scalars, FromBinaryBuffer, alignment-pinned builds), so the byte
path keeps its hand-written ranges. The exhaustive validator test still checks
that it accepts the same sequences as the other validators.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Amalgamate (#5891)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI failures: constexpr, NRVO, cppcheck, IWYU (#5891)

- json_sax_dom_error_state::is_errored() cannot be constexpr: the class
  is not a literal type (GCC 4.8, GCC -Wpedantic, Clang 3.5).
- to_binary_vector: destroy the vector writer before returning, as the
  output_vector_sink sets the size of the vector in its destructor.
  Without NRVO (MSVC Debug in C++14 mode), the vector was moved out
  before it was resized.
- parser::sax_parse: return false instead of result after the early exit
  (cppcheck identicalConditionAfterEarlyExit).
- json.hpp: export detail/exceptions.hpp for IWYU, so json_view.hpp can
  name exception_id without including a detail header.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address review: throw helpers take input_format_t, simpler type checks

- throw_on_discarded() and throw_integer_out_of_range() take an
  input_format_t and call input_format_name() themselves
- has_no_children(), set_parents() and max_size() check array, then
  object, instead of calling is_primitive() first
- parse_impl() chains like accept_impl(); sax_parse_impl() uses if/else
- std::array for the control character messages; negation<> in
  write_object_key(); clarify the is_primitive() comment

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* json_view: use exception_id in the value() overloads

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* json_sax: assert the invariant of place_container for the static analyzer

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 06:17:50 +01:00

526 lines
24 KiB
C++

// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef> // nullptr_t
#include <exception> // exception
#if JSON_DIAGNOSTICS
#include <numeric> // accumulate
#endif
#include <stdexcept> // runtime_error
#include <string> // to_string
#include <vector> // vector
#include <nlohmann/detail/value_t.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/input/position_t.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/string_concat.hpp>
// With -Wweak-vtables, Clang will complain about the exception classes as they
// have no out-of-line virtual method definitions and their vtable will be
// emitted in every translation unit. This issue cannot be fixed with a
// header-only library as there is no implementation file to move these
// functions to. As a result, we suppress this warning here to avoid client
// code stumbling over this. See https://github.com/nlohmann/json/issues/4087
// for a discussion.
#if defined(__clang__)
JSON_HEDLEY_DIAGNOSTIC_PUSH
JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wweak-vtables")
#endif
NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
////////////////
// exceptions //
////////////////
/*!
@brief the ids of the exceptions thrown by the library
@note The values are part of the public API: they are the `id` member of the
exceptions and appear in their `what()` messages.
@sa https://json.nlohmann.me/home/exceptions/
*/
enum class exception_id : int
{
// parse_error
syntax_error = 101, ///< unexpected token or invalid literal while parsing JSON
patch_not_an_array = 104, ///< a JSON Patch document is not an array of objects
patch_invalid_operation = 105, ///< a JSON Patch operation is malformed
pointer_index_leading_zero = 106, ///< a JSON Pointer array index has a leading zero
pointer_missing_slash = 107, ///< a JSON Pointer does not start with '/'
pointer_invalid_escape = 108, ///< a JSON Pointer contains an escape other than ~0 and ~1
pointer_index_not_a_number = 109, ///< a JSON Pointer array index is not a number
unexpected_end_of_input = 110, ///< a binary input ends early, or has bytes left at its end
unexpected_byte = 112, ///< a binary input contains an unexpected byte or invalid length
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
bson_unsupported_type = 114, ///< a BSON record type is not supported
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
invalid_document_image = 116, ///< json_document::load() rejects an image
// invalid_iterator
iterators_incompatible = 201, ///< the iterators of a range belong to different values
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
iterator_range_from_other_value = 203, ///< an iterator range does not belong to the value it is used with
iterator_range_out_of_range = 204, ///< an iterator range of a primitive value is not [begin, end)
iterator_out_of_range = 205, ///< an iterator of a primitive value is not begin()
iterator_range_of_null = 206, ///< an iterator range belongs to a null value
iterator_key_not_object = 207, ///< key() is called on an iterator of a non-object
iterator_subscript_on_object = 208, ///< operator[] is called on an iterator of an object
iterator_arithmetic_on_object = 209, ///< an offset operator is used on an iterator of an object
insert_range_incompatible = 210, ///< the iterators of an inserted range belong to different values
insert_range_into_itself = 211, ///< an inserted range belongs to the value it is inserted into
iterators_compare_different_values = 212, ///< iterators of different values are compared
iterator_order_on_object = 213, ///< iterators of an object are compared by order
iterator_value_unavailable = 214, ///< an iterator does not refer to a value
// type_error
object_from_non_pairs = 301, ///< an object is created from an initializer list that is not a list of pairs
type_mismatch = 302, ///< a value has the wrong type for a conversion
incompatible_reference_type = 303, ///< get_ref() is called with a reference type that does not match the value
at_wrong_type = 304, ///< at() is called on a value of the wrong type
subscript_wrong_type = 305, ///< operator[] is called on a value of the wrong type
value_wrong_type = 306, ///< value() is called on a value of the wrong type
erase_wrong_type = 307, ///< erase() is called on a value of the wrong type
push_back_wrong_type = 308, ///< push_back() or operator+= is called on a value of the wrong type
insert_wrong_type = 309, ///< insert() is called on a value of the wrong type
swap_wrong_type = 310, ///< swap() is called on a value of the wrong type
emplace_wrong_type = 311, ///< emplace() or emplace_back() is called on a value of the wrong type
update_wrong_type = 312, ///< update() is called on a value of the wrong type
unflatten_invalid_value = 313, ///< unflatten() finds conflicting paths
unflatten_not_object = 314, ///< unflatten() is called on a non-object
unflatten_value_not_primitive = 315, ///< unflatten() is called on an object with non-primitive values
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
document_binary_value = 319, ///< a binary value is stored in a json_document
document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target)
discarded_value_used = 321, ///< a discarded value is used
// out_of_range
array_index_out_of_range = 401, ///< an array index is out of range
pointer_past_the_end_index = 402, ///< a JSON Pointer uses the array index '-'
key_not_found = 403, ///< an object key is not found
pointer_unresolved = 404, ///< a JSON Pointer reference token cannot be resolved
patch_on_root = 405, ///< the root of a value has no parent, e.g. for a JSON Patch 'remove' or 'add' at the root
number_overflow = 406, ///< a number cannot be stored without overflowing to NaN or INF
integer_too_large = 407, ///< an integer cannot be represented in the binary format
container_too_large = 408, ///< the size of a container in a binary input exceeds the maximal capacity
bson_key_with_null = 409, ///< a BSON key contains U+0000
value_out_of_range = 410, ///< an enum value is undefined, or a JSON Pointer array index exceeds size_type
patch_add_parent_not_container = 411, ///< the parent of a JSON Patch 'add' target is not a container
length_too_large = 412, ///< a length does not fit into the length field of a binary format
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
document_too_large = 416, ///< a json_document input, its edit storage, or an image reaches the 32-bit (4 GiB) limit
// other_error
internal_error = 500, ///< unreachable code was reached
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
size_marker_required = 502 ///< UBJSON/BJData output with type markers requires size markers
};
/// @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_), "] ");
}
/// build the message "[json.exception.<ename>.<id>] <diagnostics><what_arg>"
template<typename BasicJsonContext>
static std::string what_impl(const std::string& ename, int id_, const std::string& what_arg, BasicJsonContext context)
{
return concat(name(ename, id_), diagnostics(context), what_arg);
}
static std::string diagnostics(std::nullptr_t /*leaf_element*/)
{
return "";
}
template<typename BasicJsonType>
static std::string diagnostics(const BasicJsonType* leaf_element)
{
#if JSON_DIAGNOSTICS
std::vector<std::string> tokens;
for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
{
const auto* parent = current->m_parent;
if (parent->is_array())
{
for (std::size_t i = 0; i < parent->m_data.m_value.array->size(); ++i)
{
if (&parent->m_data.m_value.array->operator[](i) == current)
{
tokens.emplace_back(std::to_string(i));
break;
}
}
}
else if (parent->is_object())
{
for (const auto& element : *parent->m_data.m_value.object)
{
if (&element.second == current)
{
// data() is null-terminated, so a key containing
// a null byte is cut short here rather than
// truncating the whole message at what()
tokens.emplace_back(element.first.data());
break;
}
}
}
// all other parent types (primitives) contribute no token
}
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, ") ", get_byte_positions(leaf_element));
#else
return get_byte_positions(leaf_element);
#endif
}
private:
/// an exception object as storage for error messages
std::runtime_error m;
#if JSON_DIAGNOSTIC_POSITIONS
template<typename BasicJsonType>
static std::string get_byte_positions(const BasicJsonType* leaf_element)
{
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
{
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
}
return "";
}
#else
template<typename BasicJsonType>
static std::string get_byte_positions(const BasicJsonType* leaf_element)
{
static_cast<void>(leaf_element);
return "";
}
#endif
};
/// @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<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
{
const 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<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
{
const 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()};
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), pos, what_arg, context);
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), byte_, what_arg, context);
}
/*!
@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<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
{
const std::string w = exception::what_impl("invalid_iterator", id_, what_arg, context);
return {id_, w.c_str()};
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static invalid_iterator create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
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<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
{
const std::string w = exception::what_impl("type_error", id_, what_arg, context);
return {id_, w.c_str()};
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static type_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
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<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
{
const std::string w = exception::what_impl("out_of_range", id_, what_arg, context);
return {id_, w.c_str()};
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static out_of_range create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
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<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
{
const std::string w = exception::what_impl("other_error", id_, what_arg, context);
return {id_, w.c_str()};
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static other_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private:
JSON_HEDLEY_NON_NULL(3)
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief helper function to call JSON_THROW from a template
@note JSON_THROW is a macro that, depending on the JSON_THROW_USER /
JSON_TRY_USER / JSON_NOEXCEPTION configuration, may expand to code
that does not reference its argument (e.g. `std::abort()`), which
would trigger a compilation error if the argument's type depends on
a template parameter that is otherwise unused. Wrapping the call in
a templated function avoids this and gives the compiler a single
place to see the (possibly unused) parameter.
*/
template<typename ExceptionType>
void templated_json_throw(ExceptionType exception)
{
JSON_THROW(exception);
// JSON_THROW may expand to code that discards its argument (e.g. when
// exceptions are disabled) - the cast below avoids an unused-parameter
// warning with -Werror in that case
(void)exception;
}
/*!
@brief throws because @a j does not have the type a conversion expects
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
@param[in] j the value with the wrong type
@throw type_error.302 always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const char* expected, const BasicJsonType& j)
{
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(j);
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
}
/*!
@brief throws because an operation is not supported for the type of @a j
@param[in] id_ the id of the type_error exception (at_wrong_type..update_wrong_type)
@param[in] operation the operation, e.g. "erase()"
@param[in] j the value the operation was called on
@throw type_error always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
{
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(operation);
static_cast<void>(j);
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
}
/*!
@brief throws ExceptionType with a fixed message; kept out of line so that callers do not carry the exception construction
*/
template<typename ExceptionType, typename BasicJsonContext>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_literal(const exception_id id_, const char* what_arg, BasicJsonContext context)
{
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(what_arg);
static_cast<void>(context);
JSON_THROW(ExceptionType::create(id_, what_arg, context));
}
// JSON Patch errors; constructed out of line so that patch_inplace() does not carry them
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_add_parent(const BasicJsonType& parent)
{
static_cast<void>(parent);
JSON_THROW(out_of_range::create(exception_id::patch_add_parent_not_container, concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
}
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_remove_parent(const BasicJsonType& parent)
{
static_cast<void>(parent);
JSON_THROW(out_of_range::create(exception_id::patch_remove_parent_not_container, concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
}
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_not_an_array(const BasicJsonType& j)
{
static_cast<void>(j);
JSON_THROW(parse_error::create(exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &j));
}
template<typename StringType, typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_missing_member(const StringType& op, const StringType& member, const bool string_type, const BasicJsonType& val)
{
static_cast<void>(op);
static_cast<void>(member);
static_cast<void>(string_type);
static_cast<void>(val);
// context-sensitive error message
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat((op == "op") ? std::string("operation") : concat("operation '", op, '\''), string_type ? " must have string member '" : " must have member '", member, "'"), &val));
}
template<typename StringType, typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_move_into_child(const StringType& from_path, const StringType& path, const BasicJsonType& result)
{
static_cast<void>(from_path);
static_cast<void>(path);
static_cast<void>(result);
JSON_THROW(out_of_range::create(exception_id::patch_move_into_child, concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
}
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_test_failed(const BasicJsonType& val)
{
static_cast<void>(val);
JSON_THROW(other_error::create(exception_id::patch_test_failed, concat("unsuccessful: ", val.dump()), &val));
}
template<typename StringType, typename BasicJsonType>
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_invalid_operation(const StringType& op, const BasicJsonType& val)
{
static_cast<void>(op);
static_cast<void>(val);
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat("operation value '", op, "' is invalid"), &val));
}
} // namespace detail
NLOHMANN_JSON_NAMESPACE_END
#if defined(__clang__)
JSON_HEDLEY_DIAGNOSTIC_POP
#endif