-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
alternative macro for throwing on enum serialization errors #3996
Conversation
@@ -26,7 +26,7 @@ enum class Color | |||
red, green, blue, unknown | |||
}; | |||
|
|||
NLOHMANN_JSON_SERIALIZE_ENUM(Color, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a new example instead of editing this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
🔴 Amalgamation check failed! 🔴The source code has not been amalgamated. @pabloariasal |
🔴 Amalgamation check failed! 🔴The source code has not been amalgamated. @pabloariasal |
??? example "Example 3: Strict error handling" | ||
|
||
The example shows how to define strict error handling for enum serialization. | ||
In the example conversion between `Color` and json throw an exception in case of erros (instead of defauting to the first value). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: erros instead of errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
@@ -27,6 +27,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, { | |||
The [`NLOHMANN_JSON_SERIALIZE_ENUM()` macro](../api/macros/nlohmann_json_serialize_enum.md) declares a set of | |||
`to_json()` / `from_json()` functions for type `TaskState` while avoiding repetition and boilerplate serialization code. | |||
|
|||
An alternative macro [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT()` macro](../api/macros/nlohmann_json_serialize_enum.md) can be used when a more strict error handling is preffered, throwing in case of serialization errors instead of defaulting to the first enum value defined in the macro. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: preffered instead of preferred
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@@ -230,6 +230,46 @@ | |||
e = ((it != std::end(m)) ? it : std::begin(m))->first; \ | |||
} | |||
|
|||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this only works when JSON_NOEXCEPTION
isn't defined, then that should be noted in the docs. However, I think you can just use JSON_THROW
instead of throw
and use the normal exception override behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, will note it in the docs. I think there are some arguments for not using JSON_NOEXCEPTION
:
- nested macros don't work (the preprocessor doesn't expand a macro inside another one)
- it can lead to silent and hard to debug erros if
JSON_NOEXCEPTION
is defined and the macro is used -> what should we do in that case? - the macro itself doens't make any sense if
JSON_NOEXCEPTION
is defined (hence better not define it in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nested macros don't work (the preprocessor doesn't expand a macro inside another one)
It certainly does. There are many nested macros in the library. https://godbolt.org/z/bbWv8Ye7r
it can lead to silent and hard to debug erros if JSON_NOEXCEPTION is defined and the macro is used -> what should we do in that case?
JSON_THROW
calls std::abort
when JSON_NOEXCEPTION
is defined, unless the user has overridden that by defining JSON_THROW_USER
.
the macro itself doens't make any sense if JSON_NOEXCEPTION is defined (hence better not define it in the first place.
The library already has well-defined behavior for what happens to throw calls when JSON_NOEXCEPTION
is defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, very interesting. Thanks for the godbolt link! Now I'm confused, my compiler (gcc 12, fedora 37) doesn't like that:
/home/pablo/workspace/json/tests/src/unit-conversions.cpp:1665:9: required from here
/home/pablo/workspace/json/include/nlohmann/detail/macro_scope.hpp:249:23: error: ‘JSON_THROW’ was not declared in this scope; did you mean ‘JSON_TRY’?
249 | JSON_THROW(nlohmann::detail::type_error::create(302, \
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250 | nlohmann::detail::concat("can't serialize enum value ", std::to_string(static_cast<int>(e)), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
251 | " of enum '", #ENUM_TYPE, "' to json. ", \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
252 | "Did you list the value in NLOHMANN_JSON_SERIALIZE_ENUM_STRICT?"), &j)); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/pablo/workspace/json/tests/src/unit-conversions.cpp:1618:1: note: in expansion of macro ‘NLOHMANN_JSON_SERIALIZE_ENUM_STRICT’
1618 | NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(game,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I wonder what I'm doing wrong
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My first thought was that JSON_THROW
wasn't defined yet, but that's not the case. It appears right before that macro, and it's not possible to get there without it being defined. I'm not sure why gcc would choke on that. The only other thing I can think of is that somehow you have something equivalent to #define JSON_THROW JSON_THROW
. Maybe #define JSON_THROW_USER JSON_THROW
. Can you reproduce this on compiler explorer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really clueless, would it be possible for you to try the branch out with the proposed changes locally? Looks like my preprocessor doesn't see JSON_THROW
and lets it through to the compiler...
if (it == std::end(m)) \ | ||
{ \ | ||
throw nlohmann::detail::type_error::create(302, \ | ||
nlohmann::detail::concat("can't serialize enum value ", std::to_string(static_cast<int>(e)), \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to say something about the value not being a enum value listed in NLOHMANN_JSON_SERIALIZE_ENUM_STRICT
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this is a great suggestion.
if (it == std::end(m)) \ | ||
{ \ | ||
throw nlohmann::detail::type_error::create(302, \ | ||
nlohmann::detail::concat("can't deserialize json: '", j, "' to enum '", #ENUM_TYPE "'"), &j); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, and add the string that couldn't be found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added same hint as above. The string that could not be found is already being printed, it is j
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right, missed that.
{ | ||
{ Color::green, "green" }, | ||
{ Color::blue, "blue" }, | ||
{ Color::red, "rot" } // note that serialization for yellow is missing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this say "red"
instead of "rot"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, changed
``` | ||
|
||
By default, enum values are serialized to JSON as integers. In some cases this could result in undesired behavior. If an | ||
enum is modified or re-ordered after data has been serialized to JSON, the later de-serialized JSON data may be | ||
undefined or a different enum value than was originally intended. | ||
|
||
The `NLOHMANN_JSON_SERIALIZE_ENUM` allows to define a user-defined serialization for every enumerator. | ||
The `NLOHMANN_JSON_SERIALIZE_ENUM` and `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macros allow to define a user-defined serialization for every enumerator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wording of this sentence reads really weird to me, a possible alternative:
The `NLOHMANN_JSON_SERIALIZE_ENUM` and `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macros allow to define a user-defined serialization for every enumerator. | |
The `NLOHMANN_JSON_SERIALIZE_ENUM` and `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macros allow specifying a user-defined serialization for every enumerator. |
This then matches the language in the following sentence: "first value specified in the macro".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
much better, thank you
#3992
Pull request checklist
Read the Contribution Guidelines for detailed information.
include/nlohmann
directory, runmake amalgamate
to create the single-header filessingle_include/nlohmann/json.hpp
andsingle_include/nlohmann/json_fwd.hpp
. The whole process is described here.Please don't
#ifdef
s or other means.