-
Notifications
You must be signed in to change notification settings - Fork 39
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
Use explicit operator bool when available #5
Conversation
This is unfortunately a breaking change, due to the way explicit operator bool works. The three cases that break are initializing a bool from a tribool, returning a tribool from a function returning bool, and passing a tribool to a function taking bool. None of those were tested, but they are probably in use. |
Are you saying that this PR is a breakling change to the way tribool now interacts with bool in the three cases you mentioned ? In that case why are we creating this PR ? |
I created the PR before realizing the implications. It may still be worth applying, or not, I'm not entirely sure. This breakage is actually known as it applies everywhere we switched to explicit operator bool; but here of course the type is kind-of-bool itself so things are not so clear. The upside of this change is that the conversion to bool is now properly constexpr on g++, whereas it wasn't before. |
I assume that 'explicit operator bool' means that the compiler will not look for an implicit conversion to operator bool through the safe_bool construct. If that is correct then I can not see that this PR should go through even if it does allow the conversion to bool to be a constexpr, since as you point out it disallows the implicit conversion from a tribool to a bool used by end-users to pass tribool when a bool is expected. Of course we could notify users in the next release of this potential change, in order to prepare them to use an explicit static_cast from a tribool to a bool when needed, and then make the change in the release after that. But i am still not sure whether usage in constexpr is worth the removal of the implicit conversion. |
I'm still conflicted with this. Upsides of making this change: Without this change, my safe numeric library breaks in a bunch of places with gcc. Some are due to conversion to bool not being constexpr. But there are other places where things break that seem unrelated to the constexpr issue. At least the error messages don't mention constexpr. Worse yet, some of the failures occur only in release mode, which makes it hard to track them down with the debugger. All this is resolved by this PR. Things work as I expect. Downside of making this change. If this PR is merged, programs which use implicit conversion to bool will break with a compile error. These errors are easy to fix with a static cast. So I don't see it as breaking any applications. Indeed, I have to explicitly convert to bool in a few places. Another downside is that the API for pre-C++11 compilers would be different from that of post C++11 compilers. This seems to me to be a very bad idea and indeed a recipe for disaster. The library is passing all its tests (except the constpr test with gcc) . But looking at the test suite, it's hard to verify that the tests are exhaustive. I'm really confused by all this. |
This video explains what problems arise when replacing safe_bool with explicit conversion. https://www.youtube.com/watch?v=7uIyl_tDMkM |
Recapping: certain tribool functions fail under gcc when used in a constexpr environment. There is no problem with the same code under clang. Consider the following test case:
With any version of gcc, compilation fails with the following error message:
So I modify the offending line 77 of tribool.hpp to read:
and recompile. Now the static asserts fail:
So clearly we have a problem. This was a major bitch to isolate. I'm continuing to search for a way to work around this but I'm really stuck. Anyone knowing anything about this is welcome to chime in. I don't know who is responsible for maintaining this very important (for me) module. FYI - everything works as expected when it's not a constexpr expression. |
I'll pass this on to the gcc devs. (maybe as a reduced test case) and see what they say. |
It works if I change:
to:
If you're relying on Anyway, I've reported it as https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85823 |
Oh I should have read the earlier comments not just the last two, I see using an explicit conversion to bool is the main topic anyway. |
I assume at this point it's too late to merge this for 1.68.0, correct? |
Not yet, but soon. |
The equivalent of #4 is already in |
FYI - there was thread on the list which dealt with this. This raised an objection that this was a "narrowing conversion" analogous to implicit conversion of a long to an int. I found this convincing at the time. My thinking has evolved that conversion from tribool to bool should be implicit. I've come to believe that conversion of tribool to bool is not analogous to (silent) conversion of a long to an int. It's correct conversion of a value of one type - tribool - to a value of another type - bool. So the "narrowing" argument doesn't apply since tribool and bool don't really reflect underlying mathematical types. (long and int both model real world integers). So my preference would be permitting implicit "conversion" of tribool to bool. On the other hand, I'm also aware that these kinds of implicit conversions may make code look cleaner - but sometimes hide surprising behavior. So I don't see a clean argument for one way or the other. |
Please rebase on develop so a full CI suite can run on the change. Thanks. |
3f69523
to
05c2e57
Compare
OK, done. |
Codecov Report
@@ Coverage Diff @@
## develop #5 +/- ##
========================================
Coverage 65.11% 65.11%
========================================
Files 2 2
Lines 129 129
Branches 68 68
========================================
Hits 84 84
Misses 2 2
Partials 43 43
Continue to review full report at Codecov.
|
@robertramey @pdimov it might be a good idea to satisfy yourselves that the unit tests cover the areas you are concerned with by looking at the codecov.io results. It would also be helpful if you could explicitly state whether you approve or reject the pull request as-is. I'm going to set a milestone on this for the 1.69.0 release. |
The current (master, develop) behavior allows tribool to be implicitly converted to bool where ("true" == "true") and also ("indeterminate" or "false") == "false". Is the root cause of this issue that someone may want the indeterminate value to behave differently than that, for example there are four choices I see:
What if the constructor for a tribool allowed the behavior to be changed to suit the needs of the implementer, and therefore the conversion could always safely be implicit? |
the beauty of tribool is it's simplicity and obviousness. Let's not even think of breaking this. The only question really to be discussed is whether the conversion should be implicit or explicit. As of now I'm leaning toward permitting implicit conversion. But I'm happy to defer to anyone who is willing to take long term responsibility for maintaining this valuable simple non-trivial component. BTW this would include updating the documentation with the rationale which includes the points raised here so we don't have to go down the long path again. |
I was simply suggesting that it could be up to the consumer of the class to override the default behavior and therefore have something that works for all possible cases, and thus allow for implicit conversion without fear. Items 2 through 4 would be optional behavior one could leverage if needed. |
I get this. I know that you've got the best of intentions. And there is much precedent in doing things like this to get a compromise to "please everyone". And it works. But there is a very real cost. That is increased complexity and the cost is not nothing. Were we to make such changes, then everyone who uses tribool in source code (or even looks at the source code with tribool in it) now would have to track down the documentation to figure out what it does. Since no one can remember such minutia, this would have to be done again and again. It's a significant cost and it hurts source code transparency. It's "API bloat/Brain bloat". |
Fair enough. Thanks for the input. |
looks like this was checked but never merged into the develop/master branches |
// constexpr bool res_safe_bool = tribool(true); // false | ||
// constexpr tribool xxx = (tribool(true) || tribool(indeterminate)); | ||
// static_assert(xxx, "Must be true!"); | ||
constexpr bool res_safe_bool = static_cast<bool>( tribool(true) ); |
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.
res_safe_bool isn't used by anything else - is this test sufficient? The result is not checked.
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 fact that is constexpr
is a test in itself, but we could in principle static_assert it.
I could use some help resolving: It looks like this is the cause. |
No description provided.