Fix conversion from empty json to std::optional<>#2229
Fix conversion from empty json to std::optional<>#2229karzhenkov wants to merge 11 commits intonlohmann:developfrom
Conversation
|
Is the following really needed? |
|
That assertion held true so far. If that changes now, this may indicate a breaking change. |
|
Modified |
|
There are no identical bases. Moreover, direct identical bases are not allowed. |
As i said in #2213, this is why an exception is raised when calling |
test/src/unit-conversions.cpp
Outdated
| std::optional<std::string> opt_null; | ||
|
|
||
| CHECK(json(opt_null) == j_null); | ||
| CHECK_THROWS_WITH(std::optional<std::string>(j_null), |
There was a problem hiding this comment.
Weird. In my local env, the exception would not raise. By from CI online logs , it will get the exception.
There was a problem hiding this comment.
What is special in your local environment?
There was a problem hiding this comment.
ubuntu 18.04
gcc 7.4
cmake 3.10.2
There was a problem hiding this comment.
Somewhat has changed since GCC 7.4
https://godbolt.org/z/4MfL8i
There was a problem hiding this comment.
There is probably a bug in GCC 7. When used with "-std=c++17" it may perform wrong overload resolution for user-defined conversion. In previous example and also here (with no std::optional involved) it behaves as if the implicit copy constructor is selected, whereas there is a more suitable template constructor. Other major releases of GCC (including older versions) prefer template constructor.
I do not like this approach. I cannot believe this is an issue in |
Are there specific STL implementations where the issue with |
|
It seems the issue is caused by |
|
MSVC has bad |
|
What is more, |
test/src/unit-concepts.cpp
Outdated
| // should return true | ||
| return std::is_standard_layout<D>::value; |
There was a problem hiding this comment.
Microsoft doesn't consider their implemenation wrong. Their story is about empty base optimization, backward binary compatibility and non-standard extension __declspec(empty_bases) which can be used to achieve standard behavior.
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
b1b8528 to
76a83a9
Compare
|
Same question as with #2117 - how to proceed here? |
|
Here is a "better" |
|
@karzhenkov This PR is conflicting again. Can you update? |
e0ff46c to
b7a5d99
Compare
falbrechtskirchinger
left a comment
There was a problem hiding this comment.
I'd like to evaluate this branch in one of my projects to make sure there aren't any ugly side effects. Afterward, I'll come back for a more in-depth review.
| // NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair | ||
| DOCTEST_CLANG_SUPPRESS_WARNING_PUSH | ||
| DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") | ||
| DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-macros") |
There was a problem hiding this comment.
Would it be better to just move the *_CONVERSION_WARNING macros into the #if JSON_HAS_CPP_17 block?
You're getting this warning because, in the C++11 build, the macros aren't used.
There was a problem hiding this comment.
I considered it, but I think it's hardly much better. I preferred a slightly more concise variant.
|
It's not clear if |
|
FYI: I suppressed the warnings for non-explicit constructors in Codacy. |
f1dee7b to
4168189
Compare
|
The remaining problems are related to three-way comparisons in C++20. I hope to fix them when I have time. |
|
The problem boils down to the following example, which complies in C++17 but not in C++20: #include <optional>
struct opt : std::optional<int> {};
bool operator == (const opt&, const opt&);
bool operator >= (const opt&, const opt&);
int main()
{
sizeof(opt() == opt());
sizeof(opt() >= opt());
}If any of the operator declarations is removed along with the corresponding check, the code compiles regardless of the language standard. |
|
Digging a little bit deeper, the code triggers a recursion in Adding a specilization to namespace std {
template<>
inline constexpr bool __is_optional_v<opt> = true;
}How do we work around this? |
4168189 to
3854934
Compare
It looks like a defect in the C++20 standard (or maybe in the standard library implementation). I think it is impossible to correctly define comparisons for a class derived from |
Here is possible solution (or workaround) for issues in PR #2117, #2213.
The problem is caused by "wrong" user defined conversion selected by overload resolution. Compiler selects a converting constructor for
std::optional<std::string>, whiletemplate <...> json::operator ValueTypeis needed. This behavoir is correct becausejsoncan be implicitly converted tostd::string. The constructor is unaware aboutfrom_jsonfunction."Non-template" version of conversion operator presented here "adjusts" the resolution process. However, the desired result is achieved for copy initailization only. Direct initialization still selects the converting constructor (this probably cannot be changed).