From 20bcf1015bbd55c7a8c9663ac87929b68d3d5869 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Mar 2017 14:19:05 +0100 Subject: [PATCH 1/7] :memo: cleanup after #536 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3d9123052..d0fdeb80f 100644 --- a/README.md +++ b/README.md @@ -706,6 +706,7 @@ Though it's 2016 already, the support for C++11 is still a bit sparse. Currently - GCC 4.9 - 6.0 (and possibly later) - Clang 3.4 - 3.9 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) +- Microsoft Visual C++ 2017 / Build Tools 15.1.548.43366 (and possibly later) I would be happy to learn about other compilers/versions. @@ -744,7 +745,7 @@ The following compilers are currently used in continuous integration at [Travis] | Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) | | Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) | | Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 | - +| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.1.548.43366 | ## License @@ -809,7 +810,7 @@ I deeply appreciate the help of the following people. - [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation. - [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`. - [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion. -- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable. +- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix. - [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file. - [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function. - [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing. From a58ed3cd178c89e3ae2fafe0af7a799f6ee0c234 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Mar 2017 14:47:23 +0100 Subject: [PATCH 2/7] :lipstick: cleanup --- src/json.hpp | 10 +++++----- src/json.hpp.re2c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 6ec045aaa..cc69d5422 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -12486,7 +12486,7 @@ basic_json_parser_74: { result = &result->operator[](static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -12585,7 +12585,7 @@ basic_json_parser_74: { ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -12643,7 +12643,7 @@ basic_json_parser_74: { ptr = &ptr->at(static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -12707,7 +12707,7 @@ basic_json_parser_74: { ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -12764,7 +12764,7 @@ basic_json_parser_74: { ptr = &ptr->at(static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7d21f76bb..1480a5f5e 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -11519,7 +11519,7 @@ class basic_json { result = &result->operator[](static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -11618,7 +11618,7 @@ class basic_json { ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -11676,7 +11676,7 @@ class basic_json { ptr = &ptr->at(static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -11740,7 +11740,7 @@ class basic_json { ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } @@ -11797,7 +11797,7 @@ class basic_json { ptr = &ptr->at(static_cast(std::stoi(reference_token))); } - JSON_CATCH(std::invalid_argument&) + JSON_CATCH (std::invalid_argument&) { JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); } From cc36c65a8907f67519c2024b4ed29ea8ad178798 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Mar 2017 16:22:52 +0100 Subject: [PATCH 3/7] :ambulance: made exceptions nothrow-copy-constructible #531 To have nothrow-copy-constructible exceptions, we inherit from std::runtime_error which can cope with arbitrary-length error messages. Intermediate strings are built with static functions and then passed to the actual constructor. --- README.md | 2 +- src/json.hpp | 341 +++++++++++++++++++++---------------- src/json.hpp.re2c | 341 +++++++++++++++++++++---------------- test/src/unit-noexcept.cpp | 8 + 4 files changed, 391 insertions(+), 301 deletions(-) diff --git a/README.md b/README.md index d0fdeb80f..e23e3c94a 100644 --- a/README.md +++ b/README.md @@ -894,7 +894,7 @@ $ make json_unit -Ctest $ ./test/json_unit "*" =============================================================================== -All tests passed (11202597 assertions in 47 test cases) +All tests passed (11203022 assertions in 48 test cases) ``` Alternatively, you can use [CMake](https://cmake.org) and run diff --git a/src/json.hpp b/src/json.hpp index cc69d5422..bdada276b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -116,29 +116,34 @@ namespace detail Extension of std::exception objects with a member @a id for exception ids. +@note To have nothrow-copy-constructible exceptions, we inherit from + std::runtime_error which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. + @since version 3.0.0 */ -class exception : public std::exception +class exception : public std::runtime_error { public: - /// create exception with id an explanatory string - exception(int id_, const std::string& ename, const std::string& what_arg_) - : id(id_), - what_arg("[json.exception." + ename + "." + std::to_string(id_) + "] " + what_arg_) - {} - /// returns the explanatory string virtual const char* what() const noexcept override { - return what_arg.c_str(); + return std::runtime_error::what(); } /// the id of the exception const int id; - private: - /// the explanatory string - const std::string what_arg; + protected: + exception(int id_, const char* what_arg) + : std::runtime_error(what_arg), id(id_) + {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } }; /*! @@ -184,14 +189,16 @@ class parse_error : public exception @param[in] id_ the id of the exception @param[in] byte_ the byte index where the error occured (or 0 if the position cannot be determined) - @param[in] what_arg_ the explanatory string + @param[in] what_arg the explanatory string + @return parse_error object */ - parse_error(int id_, size_t byte_, const std::string& what_arg_) - : exception(id_, "parse_error", "parse error" + - (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + - ": " + what_arg_), - byte(byte_) - {} + static parse_error create(int id_, size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } /*! @brief byte index of the parse error @@ -204,6 +211,12 @@ class parse_error : public exception MessagePack). */ const size_t byte; + + private: + parse_error(int id_, size_t byte_, const char* what_arg) + : exception(id_, what_arg), + byte(byte_) + {} }; /*! @@ -233,8 +246,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it class invalid_iterator : public exception { public: - invalid_iterator(int id_, const std::string& what_arg_) - : exception(id_, "invalid_iterator", what_arg_) + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -265,8 +285,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un class type_error : public exception { public: - type_error(int id_, const std::string& what_arg_) - : exception(id_, "type_error", what_arg_) + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + type_error(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -289,8 +316,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n class out_of_range : public exception { public: - out_of_range(int id_, const std::string& what_arg_) - : exception(id_, "out_of_range", what_arg_) + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + out_of_range(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -308,8 +342,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu class other_error : public exception { public: - other_error(int id_, const std::string& what_arg_) - : exception(id_, "other_error", what_arg_) + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + other_error(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -841,7 +882,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); } } } @@ -851,7 +892,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (not j.is_boolean()) { - JSON_THROW(type_error(302, "type must be boolean, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + j.type_name())); } b = *j.template get_ptr(); } @@ -861,7 +902,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (not j.is_string()) { - JSON_THROW(type_error(302, "type must be string, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be string, but is " + j.type_name())); } s = *j.template get_ptr(); } @@ -898,7 +939,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { if (not j.is_array()) { - JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); } arr = *j.template get_ptr(); } @@ -910,7 +951,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (not j.is_array()) { - JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); } for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) @@ -961,7 +1002,7 @@ void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { if (not j.is_array()) { - JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); } from_json_array_impl(j, arr, priority_tag<1> {}); @@ -973,7 +1014,7 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { if (not j.is_object()) { - JSON_THROW(type_error(302, "type must be object, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name())); } auto inner_object = j.template get_ptr(); @@ -1023,7 +1064,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); } } } @@ -1970,7 +2011,7 @@ class basic_json { if (t == value_t::null) { - JSON_THROW(other_error(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE } break; } @@ -2325,7 +2366,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { - JSON_THROW(type_error(301, "cannot create object from initializer list")); + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); } } @@ -2509,7 +2550,7 @@ class basic_json // make sure iterator fits the current value if (first.m_object != last.m_object) { - JSON_THROW(invalid_iterator(201, "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); } // copy type from first iterator @@ -2526,7 +2567,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(invalid_iterator(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } break; } @@ -2585,8 +2626,8 @@ class basic_json default: { - JSON_THROW(invalid_iterator(206, "cannot construct with iterators from " + - first.m_object->type_name())); + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + first.m_object->type_name())); } } @@ -3224,7 +3265,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error(302, "type must be boolean, but is " + type_name())); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); } /// get a pointer to the value (object) @@ -3336,7 +3377,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); } public: @@ -3738,12 +3779,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3785,12 +3826,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3836,12 +3877,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3887,12 +3928,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3945,7 +3986,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -3975,7 +4016,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4021,7 +4062,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4063,7 +4104,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4178,7 +4219,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4221,7 +4262,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4290,7 +4331,7 @@ class basic_json } else { - JSON_THROW(type_error(306, "cannot use value() with " + type_name())); + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } } @@ -4362,7 +4403,7 @@ class basic_json } } - JSON_THROW(type_error(306, "cannot use value() with " + type_name())); + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } /*! @@ -4515,7 +4556,7 @@ class basic_json // make sure iterator fits the current value if (this != pos.m_object) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } IteratorType result = end(); @@ -4530,7 +4571,7 @@ class basic_json { if (not pos.m_it.primitive_iterator.is_begin()) { - JSON_THROW(invalid_iterator(205, "iterator out of range")); + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); } if (is_string()) @@ -4560,7 +4601,7 @@ class basic_json default: { - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } } @@ -4622,7 +4663,7 @@ class basic_json // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) { - JSON_THROW(invalid_iterator(203, "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } IteratorType result = end(); @@ -4637,7 +4678,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(invalid_iterator(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } if (is_string()) @@ -4669,7 +4710,7 @@ class basic_json default: { - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } } @@ -4713,7 +4754,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } /*! @@ -4747,14 +4788,14 @@ class basic_json { if (idx >= size()) { - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } } @@ -5472,7 +5513,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5508,7 +5549,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5558,7 +5599,7 @@ class basic_json // push_back only works for null objects or objects if (not(is_null() or is_object())) { - JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } // transform null object into an object @@ -5658,7 +5699,7 @@ class basic_json // emplace_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error(311, "cannot use emplace_back() with " + type_name())); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); } // transform null object into an array @@ -5706,7 +5747,7 @@ class basic_json // emplace only works for null objects or arrays if (not(is_null() or is_object())) { - JSON_THROW(type_error(311, "cannot use emplace() with " + type_name())); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); } // transform null object into an object @@ -5757,7 +5798,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5766,7 +5807,7 @@ class basic_json return result; } - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } /*! @@ -5810,7 +5851,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5819,7 +5860,7 @@ class basic_json return result; } - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } /*! @@ -5857,24 +5898,24 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { - JSON_THROW(invalid_iterator(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } if (first.m_object == this or last.m_object == this) { - JSON_THROW(invalid_iterator(211, "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); } // insert to array and return iterator @@ -5915,13 +5956,13 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5988,7 +6029,7 @@ class basic_json } else { - JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); } } @@ -6021,7 +6062,7 @@ class basic_json } else { - JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); } } @@ -6054,7 +6095,7 @@ class basic_json } else { - JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); } } @@ -7999,19 +8040,19 @@ class basic_json // simple case: requested length is greater than the vector's length if (len > size or offset > size) { - JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); + JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // second case: adding offset would result in overflow if ((size > ((std::numeric_limits::max)() - offset))) { - JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); + JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // last case: reading past the end of the vector if (len + offset > size) { - JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); + JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } } @@ -8043,7 +8084,7 @@ class basic_json std::stringstream ss; ss << std::hex << static_cast(v[idx]); - JSON_THROW(parse_error(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str())); } /*! @@ -8073,7 +8114,7 @@ class basic_json std::stringstream ss; ss << std::hex << static_cast(v[idx]); - JSON_THROW(parse_error(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); } /*! @@ -8316,7 +8357,7 @@ class basic_json { std::stringstream ss; ss << std::hex << static_cast(v[current_idx]); - JSON_THROW(parse_error(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str())); } } } @@ -8816,7 +8857,7 @@ class basic_json { std::stringstream ss; ss << std::hex << static_cast(v[current_idx]); - JSON_THROW(parse_error(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str())); } } } @@ -9697,7 +9738,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } default: @@ -9707,7 +9748,7 @@ class basic_json return *m_object; } - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -9741,7 +9782,7 @@ class basic_json return m_object; } - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -9841,7 +9882,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9883,7 +9924,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9892,7 +9933,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(213, "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); } case basic_json::value_t::array: @@ -9946,7 +9987,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -10008,7 +10049,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -10035,7 +10076,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(208, "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); } case basic_json::value_t::array: @@ -10045,7 +10086,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } default: @@ -10055,7 +10096,7 @@ class basic_json return *m_object; } - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -10073,7 +10114,7 @@ class basic_json return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator(207, "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); } /*! @@ -10263,7 +10304,7 @@ class basic_json // immediately abort if stream is erroneous if (s.fail()) { - JSON_THROW(parse_error(111, 0, "bad input stream")); + JSON_THROW(parse_error::create(111, 0, "bad input stream")); } // fill buffer @@ -10330,7 +10371,7 @@ class basic_json } else { - JSON_THROW(parse_error(102, get_position(), "missing or wrong low surrogate")); + JSON_THROW(parse_error::create(102, get_position(), "missing or wrong low surrogate")); } } @@ -10364,7 +10405,7 @@ class basic_json } else { - JSON_THROW(parse_error(103, get_position(), "code points above 0x10FFFF are invalid")); + JSON_THROW(parse_error::create(103, get_position(), "code points above 0x10FFFF are invalid")); } return result; @@ -11560,7 +11601,7 @@ basic_json_parser_74: // check if stream is still good if (m_stream->fail()) { - JSON_THROW(parse_error(111, 0, "bad input stream")); + JSON_THROW(parse_error::create(111, 0, "bad input stream")); } std::getline(*m_stream, m_line_buffer_tmp, '\n'); @@ -11729,7 +11770,7 @@ basic_json_parser_74: // make sure there is a subsequent unicode if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { - JSON_THROW(parse_error(102, get_position(), "missing low surrogate")); + JSON_THROW(parse_error::create(102, get_position(), "missing low surrogate")); } // get code yyyy from uxxxx\uyyyy @@ -11742,7 +11783,7 @@ basic_json_parser_74: else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) { // we found a lone low surrogate - JSON_THROW(parse_error(102, get_position(), "missing high surrogate")); + JSON_THROW(parse_error::create(102, get_position(), "missing high surrogate")); } else { @@ -11986,7 +12027,7 @@ basic_json_parser_74: // throw in case of infinity or NAN if (not std::isfinite(result.m_value.number_float)) { - JSON_THROW(out_of_range(406, "number overflow parsing '" + get_token_string() + "'")); + JSON_THROW(out_of_range::create(406, "number overflow parsing '" + get_token_string() + "'")); } return true; @@ -12299,7 +12340,7 @@ basic_json_parser_74: "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); - JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); } } @@ -12314,7 +12355,7 @@ basic_json_parser_74: error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + "'") : lexer::token_type_name(last_token)); - JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); } } @@ -12413,7 +12454,7 @@ basic_json_parser_74: { if (is_root()) { - JSON_THROW(out_of_range(405, "JSON pointer has no parent")); + JSON_THROW(out_of_range::create(405, "JSON pointer has no parent")); } auto last = reference_tokens.back(); @@ -12431,7 +12472,7 @@ basic_json_parser_74: { if (is_root()) { - JSON_THROW(out_of_range(405, "JSON pointer has no parent")); + JSON_THROW(out_of_range::create(405, "JSON pointer has no parent")); } json_pointer result = *this; @@ -12488,7 +12529,7 @@ basic_json_parser_74: } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } @@ -12502,7 +12543,7 @@ basic_json_parser_74: */ default: { - JSON_THROW(type_error(313, "invalid value to unflatten")); + JSON_THROW(type_error::create(313, "invalid value to unflatten")); } } } @@ -12570,7 +12611,7 @@ basic_json_parser_74: // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } if (reference_token == "-") @@ -12587,7 +12628,7 @@ basic_json_parser_74: } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } } break; @@ -12595,7 +12636,7 @@ basic_json_parser_74: default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12627,15 +12668,15 @@ basic_json_parser_74: if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(out_of_range(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range::create(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check @@ -12645,14 +12686,14 @@ basic_json_parser_74: } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12691,15 +12732,15 @@ basic_json_parser_74: if (reference_token == "-") { // "-" cannot be used for const access - JSON_THROW(out_of_range(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range::create(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // use unchecked array access @@ -12709,14 +12750,14 @@ basic_json_parser_74: } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12748,15 +12789,15 @@ basic_json_parser_74: if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(out_of_range(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range::create(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check @@ -12766,14 +12807,14 @@ basic_json_parser_74: } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -12803,7 +12844,7 @@ basic_json_parser_74: // check if nonempty reference string begins with slash if (reference_string[0] != '/') { - JSON_THROW(parse_error(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); + JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); } // extract the reference tokens: @@ -12838,7 +12879,7 @@ basic_json_parser_74: (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1')) { - JSON_THROW(parse_error(108, 0, "escape character '~' must be followed with '0' or '1'")); + JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); } } @@ -12969,7 +13010,7 @@ basic_json_parser_74: { if (not value.is_object()) { - JSON_THROW(type_error(314, "only objects can be unflattened")); + JSON_THROW(type_error::create(314, "only objects can be unflattened")); } basic_json result; @@ -12979,7 +13020,7 @@ basic_json_parser_74: { if (not element.second.is_primitive()) { - JSON_THROW(type_error(315, "values in object must be primitive")); + JSON_THROW(type_error::create(315, "values in object must be primitive")); } // assign value to reference pointed to by JSON pointer; Note @@ -13364,7 +13405,7 @@ basic_json_parser_74: if (static_cast(idx) > parent.size()) { // avoid undefined behavior - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } else { @@ -13402,7 +13443,7 @@ basic_json_parser_74: } else { - JSON_THROW(out_of_range(403, "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -13415,7 +13456,7 @@ basic_json_parser_74: // type check: top level value must be an array if (not json_patch.is_array()) { - JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // iterate and apply the operations @@ -13435,13 +13476,13 @@ basic_json_parser_74: // check if desired value is present if (it == val.m_value.object->end()) { - JSON_THROW(parse_error(105, 0, error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string if (string_type and not it->second.is_string()) { - JSON_THROW(parse_error(105, 0, error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); } // no error: return value @@ -13451,7 +13492,7 @@ basic_json_parser_74: // type check: every element of the array must be an object if (not val.is_object()) { - JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members @@ -13524,7 +13565,7 @@ basic_json_parser_74: // throw an exception if test fails if (not success) { - JSON_THROW(other_error(501, "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); } break; @@ -13534,7 +13575,7 @@ basic_json_parser_74: { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error(105, 0, "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); } } } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 1480a5f5e..d5b12d2c9 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -116,29 +116,34 @@ namespace detail Extension of std::exception objects with a member @a id for exception ids. +@note To have nothrow-copy-constructible exceptions, we inherit from + std::runtime_error which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. + @since version 3.0.0 */ -class exception : public std::exception +class exception : public std::runtime_error { public: - /// create exception with id an explanatory string - exception(int id_, const std::string& ename, const std::string& what_arg_) - : id(id_), - what_arg("[json.exception." + ename + "." + std::to_string(id_) + "] " + what_arg_) - {} - /// returns the explanatory string virtual const char* what() const noexcept override { - return what_arg.c_str(); + return std::runtime_error::what(); } /// the id of the exception const int id; - private: - /// the explanatory string - const std::string what_arg; + protected: + exception(int id_, const char* what_arg) + : std::runtime_error(what_arg), id(id_) + {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } }; /*! @@ -184,14 +189,16 @@ class parse_error : public exception @param[in] id_ the id of the exception @param[in] byte_ the byte index where the error occured (or 0 if the position cannot be determined) - @param[in] what_arg_ the explanatory string + @param[in] what_arg the explanatory string + @return parse_error object */ - parse_error(int id_, size_t byte_, const std::string& what_arg_) - : exception(id_, "parse_error", "parse error" + - (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + - ": " + what_arg_), - byte(byte_) - {} + static parse_error create(int id_, size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } /*! @brief byte index of the parse error @@ -204,6 +211,12 @@ class parse_error : public exception MessagePack). */ const size_t byte; + + private: + parse_error(int id_, size_t byte_, const char* what_arg) + : exception(id_, what_arg), + byte(byte_) + {} }; /*! @@ -233,8 +246,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it class invalid_iterator : public exception { public: - invalid_iterator(int id_, const std::string& what_arg_) - : exception(id_, "invalid_iterator", what_arg_) + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -265,8 +285,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un class type_error : public exception { public: - type_error(int id_, const std::string& what_arg_) - : exception(id_, "type_error", what_arg_) + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + type_error(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -289,8 +316,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n class out_of_range : public exception { public: - out_of_range(int id_, const std::string& what_arg_) - : exception(id_, "out_of_range", what_arg_) + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + out_of_range(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -308,8 +342,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu class other_error : public exception { public: - other_error(int id_, const std::string& what_arg_) - : exception(id_, "other_error", what_arg_) + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + other_error(int id_, const char* what_arg) + : exception(id_, what_arg) {} }; @@ -841,7 +882,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); } } } @@ -851,7 +892,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (not j.is_boolean()) { - JSON_THROW(type_error(302, "type must be boolean, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + j.type_name())); } b = *j.template get_ptr(); } @@ -861,7 +902,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (not j.is_string()) { - JSON_THROW(type_error(302, "type must be string, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be string, but is " + j.type_name())); } s = *j.template get_ptr(); } @@ -898,7 +939,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { if (not j.is_array()) { - JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); } arr = *j.template get_ptr(); } @@ -910,7 +951,7 @@ void from_json(const BasicJsonType& j, std::forward_list& l) { if (not j.is_array()) { - JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); } for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) @@ -961,7 +1002,7 @@ void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { if (not j.is_array()) { - JSON_THROW(type_error(302, "type must be array, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name())); } from_json_array_impl(j, arr, priority_tag<1> {}); @@ -973,7 +1014,7 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { if (not j.is_object()) { - JSON_THROW(type_error(302, "type must be object, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name())); } auto inner_object = j.template get_ptr(); @@ -1023,7 +1064,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } default: { - JSON_THROW(type_error(302, "type must be number, but is " + j.type_name())); + JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name())); } } } @@ -1970,7 +2011,7 @@ class basic_json { if (t == value_t::null) { - JSON_THROW(other_error(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE } break; } @@ -2325,7 +2366,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { - JSON_THROW(type_error(301, "cannot create object from initializer list")); + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); } } @@ -2509,7 +2550,7 @@ class basic_json // make sure iterator fits the current value if (first.m_object != last.m_object) { - JSON_THROW(invalid_iterator(201, "iterators are not compatible")); + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); } // copy type from first iterator @@ -2526,7 +2567,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(invalid_iterator(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } break; } @@ -2585,8 +2626,8 @@ class basic_json default: { - JSON_THROW(invalid_iterator(206, "cannot construct with iterators from " + - first.m_object->type_name())); + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + first.m_object->type_name())); } } @@ -3224,7 +3265,7 @@ class basic_json return m_value.boolean; } - JSON_THROW(type_error(302, "type must be boolean, but is " + type_name())); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); } /// get a pointer to the value (object) @@ -3336,7 +3377,7 @@ class basic_json return *ptr; } - JSON_THROW(type_error(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); } public: @@ -3738,12 +3779,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3785,12 +3826,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3836,12 +3877,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3887,12 +3928,12 @@ class basic_json JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range(403, "key '" + key + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); } } else { - JSON_THROW(type_error(304, "cannot use at() with " + type_name())); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } } @@ -3945,7 +3986,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -3975,7 +4016,7 @@ class basic_json return m_value.array->operator[](idx); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4021,7 +4062,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4063,7 +4104,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4178,7 +4219,7 @@ class basic_json return m_value.object->operator[](key); } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4221,7 +4262,7 @@ class basic_json return m_value.object->find(key)->second; } - JSON_THROW(type_error(305, "cannot use operator[] with " + type_name())); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! @@ -4290,7 +4331,7 @@ class basic_json } else { - JSON_THROW(type_error(306, "cannot use value() with " + type_name())); + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } } @@ -4362,7 +4403,7 @@ class basic_json } } - JSON_THROW(type_error(306, "cannot use value() with " + type_name())); + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } /*! @@ -4515,7 +4556,7 @@ class basic_json // make sure iterator fits the current value if (this != pos.m_object) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } IteratorType result = end(); @@ -4530,7 +4571,7 @@ class basic_json { if (not pos.m_it.primitive_iterator.is_begin()) { - JSON_THROW(invalid_iterator(205, "iterator out of range")); + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); } if (is_string()) @@ -4560,7 +4601,7 @@ class basic_json default: { - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } } @@ -4622,7 +4663,7 @@ class basic_json // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) { - JSON_THROW(invalid_iterator(203, "iterators do not fit current value")); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } IteratorType result = end(); @@ -4637,7 +4678,7 @@ class basic_json { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - JSON_THROW(invalid_iterator(204, "iterators out of range")); + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } if (is_string()) @@ -4669,7 +4710,7 @@ class basic_json default: { - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } } @@ -4713,7 +4754,7 @@ class basic_json return m_value.object->erase(key); } - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } /*! @@ -4747,14 +4788,14 @@ class basic_json { if (idx >= size()) { - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - JSON_THROW(type_error(307, "cannot use erase() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } } @@ -5472,7 +5513,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5508,7 +5549,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5558,7 +5599,7 @@ class basic_json // push_back only works for null objects or objects if (not(is_null() or is_object())) { - JSON_THROW(type_error(308, "cannot use push_back() with " + type_name())); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } // transform null object into an object @@ -5658,7 +5699,7 @@ class basic_json // emplace_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error(311, "cannot use emplace_back() with " + type_name())); + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); } // transform null object into an array @@ -5706,7 +5747,7 @@ class basic_json // emplace only works for null objects or arrays if (not(is_null() or is_object())) { - JSON_THROW(type_error(311, "cannot use emplace() with " + type_name())); + JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); } // transform null object into an object @@ -5757,7 +5798,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5766,7 +5807,7 @@ class basic_json return result; } - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } /*! @@ -5810,7 +5851,7 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5819,7 +5860,7 @@ class basic_json return result; } - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } /*! @@ -5857,24 +5898,24 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { - JSON_THROW(invalid_iterator(210, "iterators do not fit")); + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } if (first.m_object == this or last.m_object == this) { - JSON_THROW(invalid_iterator(211, "passed iterators may not belong to container")); + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); } // insert to array and return iterator @@ -5915,13 +5956,13 @@ class basic_json // insert only works for arrays if (not is_array()) { - JSON_THROW(type_error(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator(202, "iterator does not fit current value")); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator @@ -5988,7 +6029,7 @@ class basic_json } else { - JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); } } @@ -6021,7 +6062,7 @@ class basic_json } else { - JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); } } @@ -6054,7 +6095,7 @@ class basic_json } else { - JSON_THROW(type_error(310, "cannot use swap() with " + type_name())); + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); } } @@ -7999,19 +8040,19 @@ class basic_json // simple case: requested length is greater than the vector's length if (len > size or offset > size) { - JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); + JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // second case: adding offset would result in overflow if ((size > ((std::numeric_limits::max)() - offset))) { - JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); + JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } // last case: reading past the end of the vector if (len + offset > size) { - JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); + JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector")); } } @@ -8043,7 +8084,7 @@ class basic_json std::stringstream ss; ss << std::hex << static_cast(v[idx]); - JSON_THROW(parse_error(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str())); } /*! @@ -8073,7 +8114,7 @@ class basic_json std::stringstream ss; ss << std::hex << static_cast(v[idx]); - JSON_THROW(parse_error(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); } /*! @@ -8316,7 +8357,7 @@ class basic_json { std::stringstream ss; ss << std::hex << static_cast(v[current_idx]); - JSON_THROW(parse_error(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str())); } } } @@ -8816,7 +8857,7 @@ class basic_json { std::stringstream ss; ss << std::hex << static_cast(v[current_idx]); - JSON_THROW(parse_error(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str())); + JSON_THROW(parse_error::create(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str())); } } } @@ -9697,7 +9738,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } default: @@ -9707,7 +9748,7 @@ class basic_json return *m_object; } - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -9741,7 +9782,7 @@ class basic_json return m_object; } - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -9841,7 +9882,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9883,7 +9924,7 @@ class basic_json // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers")); + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -9892,7 +9933,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(213, "cannot compare order of object iterators")); + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); } case basic_json::value_t::array: @@ -9946,7 +9987,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -10008,7 +10049,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators")); + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -10035,7 +10076,7 @@ class basic_json { case basic_json::value_t::object: { - JSON_THROW(invalid_iterator(208, "cannot use operator[] for object iterators")); + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); } case basic_json::value_t::array: @@ -10045,7 +10086,7 @@ class basic_json case basic_json::value_t::null: { - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } default: @@ -10055,7 +10096,7 @@ class basic_json return *m_object; } - JSON_THROW(invalid_iterator(214, "cannot get value")); + JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -10073,7 +10114,7 @@ class basic_json return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator(207, "cannot use key() for non-object iterators")); + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); } /*! @@ -10263,7 +10304,7 @@ class basic_json // immediately abort if stream is erroneous if (s.fail()) { - JSON_THROW(parse_error(111, 0, "bad input stream")); + JSON_THROW(parse_error::create(111, 0, "bad input stream")); } // fill buffer @@ -10330,7 +10371,7 @@ class basic_json } else { - JSON_THROW(parse_error(102, get_position(), "missing or wrong low surrogate")); + JSON_THROW(parse_error::create(102, get_position(), "missing or wrong low surrogate")); } } @@ -10364,7 +10405,7 @@ class basic_json } else { - JSON_THROW(parse_error(103, get_position(), "code points above 0x10FFFF are invalid")); + JSON_THROW(parse_error::create(103, get_position(), "code points above 0x10FFFF are invalid")); } return result; @@ -10593,7 +10634,7 @@ class basic_json // check if stream is still good if (m_stream->fail()) { - JSON_THROW(parse_error(111, 0, "bad input stream")); + JSON_THROW(parse_error::create(111, 0, "bad input stream")); } std::getline(*m_stream, m_line_buffer_tmp, '\n'); @@ -10762,7 +10803,7 @@ class basic_json // make sure there is a subsequent unicode if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { - JSON_THROW(parse_error(102, get_position(), "missing low surrogate")); + JSON_THROW(parse_error::create(102, get_position(), "missing low surrogate")); } // get code yyyy from uxxxx\uyyyy @@ -10775,7 +10816,7 @@ class basic_json else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) { // we found a lone low surrogate - JSON_THROW(parse_error(102, get_position(), "missing high surrogate")); + JSON_THROW(parse_error::create(102, get_position(), "missing high surrogate")); } else { @@ -11019,7 +11060,7 @@ class basic_json // throw in case of infinity or NAN if (not std::isfinite(result.m_value.number_float)) { - JSON_THROW(out_of_range(406, "number overflow parsing '" + get_token_string() + "'")); + JSON_THROW(out_of_range::create(406, "number overflow parsing '" + get_token_string() + "'")); } return true; @@ -11332,7 +11373,7 @@ class basic_json "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); - JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); } } @@ -11347,7 +11388,7 @@ class basic_json error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + "'") : lexer::token_type_name(last_token)); - JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg)); + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); } } @@ -11446,7 +11487,7 @@ class basic_json { if (is_root()) { - JSON_THROW(out_of_range(405, "JSON pointer has no parent")); + JSON_THROW(out_of_range::create(405, "JSON pointer has no parent")); } auto last = reference_tokens.back(); @@ -11464,7 +11505,7 @@ class basic_json { if (is_root()) { - JSON_THROW(out_of_range(405, "JSON pointer has no parent")); + JSON_THROW(out_of_range::create(405, "JSON pointer has no parent")); } json_pointer result = *this; @@ -11521,7 +11562,7 @@ class basic_json } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } @@ -11535,7 +11576,7 @@ class basic_json */ default: { - JSON_THROW(type_error(313, "invalid value to unflatten")); + JSON_THROW(type_error::create(313, "invalid value to unflatten")); } } } @@ -11603,7 +11644,7 @@ class basic_json // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } if (reference_token == "-") @@ -11620,7 +11661,7 @@ class basic_json } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } } break; @@ -11628,7 +11669,7 @@ class basic_json default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11660,15 +11701,15 @@ class basic_json if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(out_of_range(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range::create(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check @@ -11678,14 +11719,14 @@ class basic_json } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11724,15 +11765,15 @@ class basic_json if (reference_token == "-") { // "-" cannot be used for const access - JSON_THROW(out_of_range(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range::create(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // use unchecked array access @@ -11742,14 +11783,14 @@ class basic_json } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11781,15 +11822,15 @@ class basic_json if (reference_token == "-") { // "-" always fails the range check - JSON_THROW(out_of_range(402, "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + JSON_THROW(out_of_range::create(402, "array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (reference_token.size() > 1 and reference_token[0] == '0') { - JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'")); + JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check @@ -11799,14 +11840,14 @@ class basic_json } JSON_CATCH (std::invalid_argument&) { - JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number")); + JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: { - JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'")); + JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } } @@ -11836,7 +11877,7 @@ class basic_json // check if nonempty reference string begins with slash if (reference_string[0] != '/') { - JSON_THROW(parse_error(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); + JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); } // extract the reference tokens: @@ -11871,7 +11912,7 @@ class basic_json (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1')) { - JSON_THROW(parse_error(108, 0, "escape character '~' must be followed with '0' or '1'")); + JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); } } @@ -12002,7 +12043,7 @@ class basic_json { if (not value.is_object()) { - JSON_THROW(type_error(314, "only objects can be unflattened")); + JSON_THROW(type_error::create(314, "only objects can be unflattened")); } basic_json result; @@ -12012,7 +12053,7 @@ class basic_json { if (not element.second.is_primitive()) { - JSON_THROW(type_error(315, "values in object must be primitive")); + JSON_THROW(type_error::create(315, "values in object must be primitive")); } // assign value to reference pointed to by JSON pointer; Note @@ -12397,7 +12438,7 @@ class basic_json if (static_cast(idx) > parent.size()) { // avoid undefined behavior - JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } else { @@ -12435,7 +12476,7 @@ class basic_json } else { - JSON_THROW(out_of_range(403, "key '" + last_path + "' not found")); + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -12448,7 +12489,7 @@ class basic_json // type check: top level value must be an array if (not json_patch.is_array()) { - JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // iterate and apply the operations @@ -12468,13 +12509,13 @@ class basic_json // check if desired value is present if (it == val.m_value.object->end()) { - JSON_THROW(parse_error(105, 0, error_msg + " must have member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string if (string_type and not it->second.is_string()) { - JSON_THROW(parse_error(105, 0, error_msg + " must have string member '" + member + "'")); + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); } // no error: return value @@ -12484,7 +12525,7 @@ class basic_json // type check: every element of the array must be an object if (not val.is_object()) { - JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects")); + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members @@ -12557,7 +12598,7 @@ class basic_json // throw an exception if test fails if (not success) { - JSON_THROW(other_error(501, "unsuccessful: " + val.dump())); + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); } break; @@ -12567,7 +12608,7 @@ class basic_json { // op must be "add", "remove", "replace", "move", "copy", or // "test" - JSON_THROW(parse_error(105, 0, "operation value '" + op + "' is invalid")); + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); } } } diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp index 898e77965..ed362f65c 100644 --- a/test/src/unit-noexcept.cpp +++ b/test/src/unit-noexcept.cpp @@ -57,3 +57,11 @@ static_assert(noexcept(json(pod{})), ""); static_assert(noexcept(j.get()), ""); static_assert(not noexcept(j.get()), ""); static_assert(noexcept(json(pod{})), ""); + +// for ERR60-CPP (https://github.com/nlohmann/json/issues/531) +static_assert(std::is_nothrow_copy_constructible::value, "json::exception must be nothrow copy constructible"); +static_assert(std::is_nothrow_copy_constructible::value, "json::parse_error must be nothrow copy constructible"); +static_assert(std::is_nothrow_copy_constructible::value, "json::invalid_iterator must be nothrow copy constructible"); +static_assert(std::is_nothrow_copy_constructible::value, "json::type_error must be nothrow copy constructible"); +static_assert(std::is_nothrow_copy_constructible::value, "json::out_of_range must be nothrow copy constructible"); +static_assert(std::is_nothrow_copy_constructible::value, "json::other_error must be nothrow copy constructible"); From c333679a9653119ddf19613d3fdc2cb1d42e2a26 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Mar 2017 17:25:39 +0100 Subject: [PATCH 4/7] :hammer: small refactoring The solution with a std::runtime_error member is more elegant. It allows to have std::exception as base class again. However, I still have no idea why GCC thinks the copy constructor may throw... --- src/json.hpp | 9 ++++++--- src/json.hpp.re2c | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index bdada276b..5ee3edade 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -123,13 +123,13 @@ Extension of std::exception objects with a member @a id for exception ids. @since version 3.0.0 */ -class exception : public std::runtime_error +class exception : public std::exception { public: /// returns the explanatory string virtual const char* what() const noexcept override { - return std::runtime_error::what(); + return m.what(); } /// the id of the exception @@ -137,13 +137,16 @@ class exception : public std::runtime_error protected: exception(int id_, const char* what_arg) - : std::runtime_error(what_arg), id(id_) + : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) { return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } + + private: + std::runtime_error m; }; /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index d5b12d2c9..a51f77e77 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -123,13 +123,13 @@ Extension of std::exception objects with a member @a id for exception ids. @since version 3.0.0 */ -class exception : public std::runtime_error +class exception : public std::exception { public: /// returns the explanatory string virtual const char* what() const noexcept override { - return std::runtime_error::what(); + return m.what(); } /// the id of the exception @@ -137,13 +137,16 @@ class exception : public std::runtime_error protected: exception(int id_, const char* what_arg) - : std::runtime_error(what_arg), id(id_) + : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) { return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } + + private: + std::runtime_error m; }; /*! From cf7786887c7ca80d86112f6b9f0aeba8cb1f2f9c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Mar 2017 17:35:57 +0100 Subject: [PATCH 5/7] :hammer: fixed check for is_nothrow_copy_constructible We now only demand our exceptions to be is_nothrow_copy_constructible if std::runtime_exception is. --- test/src/unit-noexcept.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp index ed362f65c..f2fbc8e66 100644 --- a/test/src/unit-noexcept.cpp +++ b/test/src/unit-noexcept.cpp @@ -58,10 +58,19 @@ static_assert(noexcept(j.get()), ""); static_assert(not noexcept(j.get()), ""); static_assert(noexcept(json(pod{})), ""); -// for ERR60-CPP (https://github.com/nlohmann/json/issues/531) -static_assert(std::is_nothrow_copy_constructible::value, "json::exception must be nothrow copy constructible"); -static_assert(std::is_nothrow_copy_constructible::value, "json::parse_error must be nothrow copy constructible"); -static_assert(std::is_nothrow_copy_constructible::value, "json::invalid_iterator must be nothrow copy constructible"); -static_assert(std::is_nothrow_copy_constructible::value, "json::type_error must be nothrow copy constructible"); -static_assert(std::is_nothrow_copy_constructible::value, "json::out_of_range must be nothrow copy constructible"); -static_assert(std::is_nothrow_copy_constructible::value, "json::other_error must be nothrow copy constructible"); +TEST_CASE("runtime checks") +{ + SECTION("nothrow-copy-constructible exceptions") + { + // for ERR60-CPP (https://github.com/nlohmann/json/issues/531) + if (std::is_nothrow_copy_constructible::value) + { + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + } + } +} From 53b501a785f94250fe7b359b8ba6288d495aad5d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 25 Mar 2017 23:31:03 +0100 Subject: [PATCH 6/7] :hammer: cleanup --- src/json.hpp | 64 +++++++++++++++++++------------------- src/json.hpp.re2c | 62 ++++++++++++++++++------------------ test/src/unit-noexcept.cpp | 21 +++++++------ 3 files changed, 74 insertions(+), 73 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 5ee3edade..522c601de 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -116,7 +116,7 @@ namespace detail Extension of std::exception objects with a member @a id for exception ids. -@note To have nothrow-copy-constructible exceptions, we inherit from +@note To have nothrow-copy-constructible exceptions, we internally use std::runtime_error which can cope with arbitrary-length error messages. Intermediate strings are built with static functions and then passed to the actual constructor. @@ -136,16 +136,17 @@ class exception : public std::exception const int id; protected: - exception(int id_, const char* what_arg) + exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) : id(id_), m(what_arg) {} - static std::string name(const std::string& ename, int id_) + static std::string name(const std::string& ename, int id) { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + return "[json.exception." + ename + "." + std::to_string(id) + "] "; } private: + /// an exception object as storage for error messages std::runtime_error m; }; @@ -184,23 +185,23 @@ json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last @since version 3.0.0 */ -class parse_error : public exception +class parse_error : private exception { public: /*! @brief create a parse error exception - @param[in] id_ the id of the exception + @param[in] id the id of the exception @param[in] byte_ the byte index where the error occured (or 0 if the position cannot be determined) @param[in] what_arg the explanatory string @return parse_error object */ - static parse_error create(int id_, size_t byte_, const std::string& what_arg) + static parse_error create(int id, size_t byte_, const std::string& what_arg) { - std::string w = exception::name("parse_error", id_) + "parse error" + + std::string w = exception::name("parse_error", id) + "parse error" + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + ": " + what_arg; - return parse_error(id_, byte_, w.c_str()); + return parse_error(id, byte_, w.c_str()); } /*! @@ -216,9 +217,8 @@ class parse_error : public exception const size_t byte; private: - parse_error(int id_, size_t byte_, const char* what_arg) - : exception(id_, what_arg), - byte(byte_) + parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg), byte(byte_) {} }; @@ -249,15 +249,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it class invalid_iterator : public exception { public: - static invalid_iterator create(int id_, const std::string& what_arg) + static invalid_iterator create(int id, const std::string& what_arg) { - std::string w = exception::name("invalid_iterator", id_) + what_arg; - return invalid_iterator(id_, w.c_str()); + std::string w = exception::name("invalid_iterator", id) + what_arg; + return invalid_iterator(id, w.c_str()); } private: - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) + invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; @@ -288,15 +288,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un class type_error : public exception { public: - static type_error create(int id_, const std::string& what_arg) + static type_error create(int id, const std::string& what_arg) { - std::string w = exception::name("type_error", id_) + what_arg; - return type_error(id_, w.c_str()); + std::string w = exception::name("type_error", id) + what_arg; + return type_error(id, w.c_str()); } private: - type_error(int id_, const char* what_arg) - : exception(id_, what_arg) + type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; @@ -319,15 +319,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n class out_of_range : public exception { public: - static out_of_range create(int id_, const std::string& what_arg) + static out_of_range create(int id, const std::string& what_arg) { - std::string w = exception::name("out_of_range", id_) + what_arg; - return out_of_range(id_, w.c_str()); + std::string w = exception::name("out_of_range", id) + what_arg; + return out_of_range(id, w.c_str()); } private: - out_of_range(int id_, const char* what_arg) - : exception(id_, what_arg) + out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; @@ -345,15 +345,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu class other_error : public exception { public: - static other_error create(int id_, const std::string& what_arg) + static other_error create(int id, const std::string& what_arg) { - std::string w = exception::name("other_error", id_) + what_arg; - return other_error(id_, w.c_str()); + std::string w = exception::name("other_error", id) + what_arg; + return other_error(id, w.c_str()); } private: - other_error(int id_, const char* what_arg) - : exception(id_, what_arg) + other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index a51f77e77..c7243ca4c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -116,7 +116,7 @@ namespace detail Extension of std::exception objects with a member @a id for exception ids. -@note To have nothrow-copy-constructible exceptions, we inherit from +@note To have nothrow-copy-constructible exceptions, we internally use std::runtime_error which can cope with arbitrary-length error messages. Intermediate strings are built with static functions and then passed to the actual constructor. @@ -136,16 +136,17 @@ class exception : public std::exception const int id; protected: - exception(int id_, const char* what_arg) + exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) : id(id_), m(what_arg) {} - static std::string name(const std::string& ename, int id_) + static std::string name(const std::string& ename, int id) { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + return "[json.exception." + ename + "." + std::to_string(id) + "] "; } private: + /// an exception object as storage for error messages std::runtime_error m; }; @@ -189,18 +190,18 @@ class parse_error : public exception public: /*! @brief create a parse error exception - @param[in] id_ the id of the exception + @param[in] id the id of the exception @param[in] byte_ the byte index where the error occured (or 0 if the position cannot be determined) @param[in] what_arg the explanatory string @return parse_error object */ - static parse_error create(int id_, size_t byte_, const std::string& what_arg) + static parse_error create(int id, size_t byte_, const std::string& what_arg) { - std::string w = exception::name("parse_error", id_) + "parse error" + + std::string w = exception::name("parse_error", id) + "parse error" + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + ": " + what_arg; - return parse_error(id_, byte_, w.c_str()); + return parse_error(id, byte_, w.c_str()); } /*! @@ -216,9 +217,8 @@ class parse_error : public exception const size_t byte; private: - parse_error(int id_, size_t byte_, const char* what_arg) - : exception(id_, what_arg), - byte(byte_) + parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg), byte(byte_) {} }; @@ -249,15 +249,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it class invalid_iterator : public exception { public: - static invalid_iterator create(int id_, const std::string& what_arg) + static invalid_iterator create(int id, const std::string& what_arg) { - std::string w = exception::name("invalid_iterator", id_) + what_arg; - return invalid_iterator(id_, w.c_str()); + std::string w = exception::name("invalid_iterator", id) + what_arg; + return invalid_iterator(id, w.c_str()); } private: - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) + invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; @@ -288,15 +288,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un class type_error : public exception { public: - static type_error create(int id_, const std::string& what_arg) + static type_error create(int id, const std::string& what_arg) { - std::string w = exception::name("type_error", id_) + what_arg; - return type_error(id_, w.c_str()); + std::string w = exception::name("type_error", id) + what_arg; + return type_error(id, w.c_str()); } private: - type_error(int id_, const char* what_arg) - : exception(id_, what_arg) + type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; @@ -319,15 +319,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n class out_of_range : public exception { public: - static out_of_range create(int id_, const std::string& what_arg) + static out_of_range create(int id, const std::string& what_arg) { - std::string w = exception::name("out_of_range", id_) + what_arg; - return out_of_range(id_, w.c_str()); + std::string w = exception::name("out_of_range", id) + what_arg; + return out_of_range(id, w.c_str()); } private: - out_of_range(int id_, const char* what_arg) - : exception(id_, what_arg) + out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; @@ -345,15 +345,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu class other_error : public exception { public: - static other_error create(int id_, const std::string& what_arg) + static other_error create(int id, const std::string& what_arg) { - std::string w = exception::name("other_error", id_) + what_arg; - return other_error(id_, w.c_str()); + std::string w = exception::name("other_error", id) + what_arg; + return other_error(id, w.c_str()); } private: - other_error(int id_, const char* what_arg) - : exception(id_, what_arg) + other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + : exception(id, what_arg) {} }; diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp index f2fbc8e66..8269574e1 100644 --- a/test/src/unit-noexcept.cpp +++ b/test/src/unit-noexcept.cpp @@ -62,15 +62,16 @@ TEST_CASE("runtime checks") { SECTION("nothrow-copy-constructible exceptions") { - // for ERR60-CPP (https://github.com/nlohmann/json/issues/531) - if (std::is_nothrow_copy_constructible::value) - { - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - } + // for ERR60-CPP (https://github.com/nlohmann/json/issues/531): + // Exceptions should be nothrow-copy-constructible. However, compilers + // treat std::runtime_exception differently in this regard. Therefore, + // we can only demand nothrow-copy-constructibility for our exceptions + // if std::runtime_exception is. + CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); } } From 333619430601807e2807b5d63eae831f435e7d00 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 26 Mar 2017 12:23:10 +0200 Subject: [PATCH 7/7] :hammer: reverted changes that led to Travis failures --- src/json.hpp | 14 +++++++------- src/json.hpp.re2c | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 522c601de..cae427ef3 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -136,7 +136,7 @@ class exception : public std::exception const int id; protected: - exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} @@ -185,7 +185,7 @@ json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last @since version 3.0.0 */ -class parse_error : private exception +class parse_error : public exception { public: /*! @@ -217,7 +217,7 @@ class parse_error : private exception const size_t byte; private: - parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + parse_error(int id, size_t byte_, const char* what_arg) : exception(id, what_arg), byte(byte_) {} }; @@ -256,7 +256,7 @@ class invalid_iterator : public exception } private: - invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + invalid_iterator(int id, const char* what_arg) : exception(id, what_arg) {} }; @@ -295,7 +295,7 @@ class type_error : public exception } private: - type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + type_error(int id, const char* what_arg) : exception(id, what_arg) {} }; @@ -326,7 +326,7 @@ class out_of_range : public exception } private: - out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + out_of_range(int id, const char* what_arg) : exception(id, what_arg) {} }; @@ -352,7 +352,7 @@ class other_error : public exception } private: - other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + other_error(int id, const char* what_arg) : exception(id, what_arg) {} }; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index c7243ca4c..3aabece5c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -136,7 +136,7 @@ class exception : public std::exception const int id; protected: - exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} @@ -217,7 +217,7 @@ class parse_error : public exception const size_t byte; private: - parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + parse_error(int id, size_t byte_, const char* what_arg) : exception(id, what_arg), byte(byte_) {} }; @@ -256,7 +256,7 @@ class invalid_iterator : public exception } private: - invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + invalid_iterator(int id, const char* what_arg) : exception(id, what_arg) {} }; @@ -295,7 +295,7 @@ class type_error : public exception } private: - type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + type_error(int id, const char* what_arg) : exception(id, what_arg) {} }; @@ -326,7 +326,7 @@ class out_of_range : public exception } private: - out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + out_of_range(int id, const char* what_arg) : exception(id, what_arg) {} }; @@ -352,7 +352,7 @@ class other_error : public exception } private: - other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg))) + other_error(int id, const char* what_arg) : exception(id, what_arg) {} };