Repository navigation
Use user-defined exceptions #244
Copy link
Copy link
Closed
Description
After a small survey on Reddit it seems to be a good idea to replace the standard exceptions used in the library right now with user-defined exceptions.
The motivations for this are:
- Better differentiation between exceptions raised by the STL and those raised by the JSON library.
- Clearer semantics of the exceptions rather and the possibility of better diagnosis and documentation.
In the course of this ticket, the list of currently used exceptions need to be classified into user-defined exception classes:
throw std::domain_error("JSON pointer has no parent");
throw std::domain_error("JSON pointer must be empty or begin with '/'");
throw std::domain_error("array index must not begin with '0'");
throw std::domain_error("cannot compare iterators of different containers");
throw std::domain_error("cannot compare order of object iterators");
throw std::domain_error("cannot create object from initializer list");
throw std::domain_error("cannot use at() with " + type_name());
throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
throw std::domain_error("cannot use erase() with " + type_name());
throw std::domain_error("cannot use insert() with " + type_name());
throw std::domain_error("cannot use key() for non-object iterators");
throw std::domain_error("cannot use offsets with object iterators");
throw std::domain_error("cannot use operator[] for object iterators");
throw std::domain_error("cannot use operator[] with " + type_name());
throw std::domain_error("cannot use push_back() with " + type_name());
throw std::domain_error("cannot use swap() with " + type_name());
throw std::domain_error("cannot use value() with " + type_name());
throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
throw std::domain_error("invalid value to unflatten");
throw std::domain_error("iterator does not fit current value");
throw std::domain_error("iterators are not compatible");
throw std::domain_error("iterators do not fit current value");
throw std::domain_error("iterators do not fit");
throw std::domain_error("only objects can be unflattened");
throw std::domain_error("passed iterators may not belong to container");
throw std::domain_error("type must be array, but is " + type_name());
throw std::domain_error("type must be number, but is " + type_name());
throw std::domain_error("type must be object, but is " + type_name());
throw std::domain_error("type must be string, but is " + type_name());
throw std::domain_error("unsuccessful: " + val.dump());
throw std::domain_error("values in object must be primitive");
throw std::invalid_argument("JSON patch must be an array of objects");
throw std::invalid_argument("missing low surrogate");
throw std::invalid_argument("missing or wrong low surrogate");
throw std::invalid_argument("operation value '" + op + "' is invalid");
throw std::invalid_argument(error_msg + " must have member '" + member + "'");
throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
throw std::invalid_argument(error_msg);
throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
throw std::out_of_range("array index '-' (" +
throw std::out_of_range("cannot get value");
throw std::out_of_range("code points above 0x10FFFF are invalid");
throw std::out_of_range("iterator out of range");
throw std::out_of_range("iterators out of range");
throw std::out_of_range("key '" + key + "' not found");
throw std::out_of_range("key '" + last_path + "' not found");
throw std::out_of_range("unresolved reference token '" + reference_token + "'");Furthermore, STL exceptions need to be caught and rethrown as user-defined exception in several cases.
Activity
whackashoe commentedon May 14, 2016
For simplicity's sake I think it is much easier to deal with libs that have fewer rather than more different types of exceptions (as long as there isn't weird overlaps). So, here's a few suggestions on how that could be done:
These could be
type_errorCould be
invalid_iteratorCould be
not_supportedorinvalid_actionorinvalid_methodCould be
lookup_errororkey_errorAnyways, this way there doesn't need to be this large tree of possible exceptions to keep in head if they are grouped a bit.
gregmarr commentedon May 14, 2016
I'd recommend that if you add multiple exception classes derived from any std::exception related class, that you have your own base class first, so that they can all be handled at once if the user desires.
nlohmann commentedon Jul 3, 2016
I started a feature branch for user-defined exceptions. There is now a class
basic_json::exceptionwith a subclassinvalid_iteratorwhich is used in all iterator-related scenarios. The user-defined exception class uses an integer error code to facility better documentation of exceptions - see Doxygen-doucumentation for classbasic_json::exception.I shall replace more standard exceptions in the next days. I would be happy for comments!
markand commentedon Aug 8, 2016
@whackashoe there are various reasons why we should add own exceptions. For example when you access a property that does not exist or is it invalid, you can throw an exception like
missing_propertyorinvalid_propertywhere you can bundle the property name which is valuable for the user of the library.The general error message that comes with
what()is always handy, but sometimes you want more information like the line number in a file, the column, many things like that and exceptions are just the way to go for this purpose.nlohmann commentedon Oct 16, 2016
@krzysztofwos, thanks for the suggestions. Here are my thoughts:
what()string.what()string rather general which simplifies the code. The in-depth explanation can then be moved to the documentation. (Then again, do we actually need awhat()string?) At the same time, users of the library are free to use as much additional information to compose their own diagnostic messages.basic_jsonvariable to hold all information on the exception. What do you think? Is it a good idea to create exception objects that again rely on dynamic memory?gregmarr commentedon Oct 16, 2016
As long as it's not due to an out of memory condition, then you should be okay with dynamic memory allocation.
4 remaining items
more user-defined exceptions (#244)
more user-defined exceptions (#244)
🚧 removed the last std exceptions (#244)
📝 finished documenting all user-defined exceptions (#244)
nlohmann commentedon Nov 16, 2016
The user-defined exceptions are done now, see the new feature branch. This branch also contains the option to switch off assertions (#296).
jlisee commentedon Feb 15, 2017
This is really cool to see, do you have any idea when you would be merging this in?
nlohmann commentedon Feb 15, 2017
Not really. There are still 1-2 issues that need to be fixed before I can start with the 3.0.0 release.
🔨 started with user-defined exceptions #301 #244
nlohmann commentedon Mar 8, 2017
This issue is nearly complete. If anyone would like to have a look at the soon-to-be-merged state, please have a look at branch https://github.com/nlohmann/json/tree/feature/exceptions_3.0.0. It contains user-defined exceptions, and only the documentation needs some final touches.
💥 implemented new handling of NaN and INF #70 #329 #388
nlohmann commentedon Mar 12, 2017
Merged this feature into
develop. All there is left to do is to document the changes between old and new exceptions in the wiki page.