Repository navigation
Add conversion from/to std::optional #2117
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
Changes from all commits
df30a0e
1359c56
713038f
1b846c9
a2fea87
6b31375
71c80cc
007c6c4
f25bf31
b61d563
fedf82c
5e55158
ebb63bd
f1913fe
384442e
7ffd3ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1703,6 +1703,65 @@ TEST_CASE("JSON to enum mapping") | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| #ifdef JSON_HAS_CPP_17 | ||||||
| TEST_CASE("std::optional") | ||||||
| { | ||||||
| SECTION("null") | ||||||
| { | ||||||
| json j_null; | ||||||
| std::optional<std::string> opt_null; | ||||||
|
|
||||||
| CHECK(json(opt_null) == j_null); | ||||||
| CHECK(std::optional<std::string>(j_null) == std::nullopt); | ||||||
| } | ||||||
|
|
||||||
| SECTION("string") | ||||||
| { | ||||||
| json j_string = "string"; | ||||||
| std::optional<std::string> opt_string = "string"; | ||||||
|
|
||||||
| CHECK(json(opt_string) == j_string); | ||||||
| CHECK(std::optional<std::string>(j_string) == opt_string); | ||||||
| } | ||||||
|
|
||||||
| SECTION("bool") | ||||||
| { | ||||||
| json j_bool = true; | ||||||
| std::optional<bool> opt_bool = true; | ||||||
|
|
||||||
| CHECK(json(opt_bool) == j_bool); | ||||||
| CHECK(std::optional<bool>(j_bool) == opt_bool); | ||||||
| } | ||||||
|
|
||||||
| SECTION("number") | ||||||
| { | ||||||
| json j_number = 1; | ||||||
| std::optional<int> opt_int = 1; | ||||||
|
|
||||||
| CHECK(json(opt_int) == j_number); | ||||||
| CHECK(std::optional<int>(j_number) == opt_int); | ||||||
| } | ||||||
|
|
||||||
| SECTION("array") | ||||||
| { | ||||||
| json j_array = {1, 2, nullptr}; | ||||||
| std::vector<std::optional<int>> opt_array = {{1, 2, std::nullopt}}; | ||||||
|
|
||||||
| CHECK(json(opt_array) == j_array); | ||||||
| CHECK(std::vector<std::optional<int>>(j_array) == opt_array); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Direct initialization of
Suggested change
Such kind of initialization of |
||||||
| } | ||||||
|
|
||||||
| SECTION("object") | ||||||
| { | ||||||
| json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}}; | ||||||
| std::map<std::string, std::optional<int>> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; | ||||||
|
|
||||||
| CHECK(json(opt_object) == j_object); | ||||||
| CHECK(std::map<std::string, std::optional<int>>(j_object) == opt_object); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Direct initialization of
Suggested change
|
||||||
| } | ||||||
| } | ||||||
| #endif | ||||||
|
|
||||||
| #ifdef JSON_HAS_CPP_17 | ||||||
| #undef JSON_HAS_CPP_17 | ||||||
| #endif | ||||||
|
|
||||||
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.
Initialization of
optional<T>from emptyjsonshould be considered as atype_error:The reason is as follows. If
jsonis implicitly convertible toT, then initialization ofoptional<T>fromjsonalmost necessarily goes through the "forwarding" constructor:This constructor provides the best parameter/argument match by value category, constness and a type. Its parameter binds directly to
jsonin any form. The only "weakness" of the forwarding constructor is that it is a template. A conversion function that would be considered as a better candidate for overload resolution must be non-templated, but this is obviously unacceptable.The forwarding constructor converts
jsontoT(initializes the latter from the former) and stores the result, so the constructedoptional<T>cannot be empty. Such a conversion from emptyjsontoTis atype error(even ifTitself isoptional<X>, by induction).The non-standard overload resolution performed by GCC and Clang when enabling C++17 does not change the outcome. The only way to achieve the "natural" behavior is to tune constructors of
std::optional<T>, which is beyond the scope of the project.Uh oh!
There was an error while loading. Please reload this page.
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.
There is also the usual way to explicitly convert
jsontooptional<T>. The following will work:Uh oh!
There was an error while loading. Please reload this page.
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.
Essentially, there is nothing new here. @dota17 proposed the same in #2213.
However, this approach (treat such initialization as
type_error) introduces some inconsistency:Perhaps it makes sense to throw
type_errorin the first case too?The "natural" conversion could be explicitly implemented by dedicated member function:
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.
Moreover, as it turns out, this dedicated member function is the only meaningful thing
nlohmann::basic_jsoncan provide to supportstd::optional. Overloads ofget<std::optional<T>>should probably be disabled to prevent confusion.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 think the behavior is counter-intuitive. JSON's
nullis usually considered as "unset" value (in contrast to an empty value). Therefore, I would expectnullto convert tostd::nulloptof any optional type.