-
-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
Add conversion from/to std::optional #2117
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); | ||||||
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); | ||||||
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 emptyjson
should be considered as atype_error
:The reason is as follows. If
json
is implicitly convertible toT
, then initialization ofoptional<T>
fromjson
almost 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
json
in 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
json
toT
(initializes the latter from the former) and stores the result, so the constructedoptional<T>
cannot be empty. Such a conversion from emptyjson
toT
is atype error
(even ifT
itself 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.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
json
tooptional<T>
. The following will work: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_error
in 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_json
can 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
null
is usually considered as "unset" value (in contrast to an empty value). Therefore, I would expectnull
to convert tostd::nullopt
of any optional type.