-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Replace use of TriStateBool with boost::optional. #7906
Conversation
boost::optional (and std::optional in C++17) provide a more general mechanism for specifying tri-state values that works for types other than bool.
Seems like this Travis build does not work with boost features from 4/2014? What is the minimum boost version qbt is supposed to support? |
older "get" methods. These are mostly functionally equivalent, and are supported on older versions of Boost.
http://www.boost.org/doc/libs/1_65_1/libs/optional/doc/html/boost_optional/tutorial/a_note_about_optional_bool_.html |
I read that note and considered boost::tribool at the onset, but I think that optional is a better fit.
|
IIRC version 1.35 is defined in configure.ac, what minimum boost version is in line with the namings in std::optional? |
src/app/cmdoptions.cpp
Outdated
else if (skipDialog == TriStateBool::False) { | ||
result.append(QLatin1String("@skipDialog=0")); | ||
if (skipDialog) { | ||
if (skipDialog.get()) |
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.
IMO you should decide on one: operator*
or value()
, I saw both are used.
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.
We can't use value() since it requires a newer version of boost. Probably better to use operator* then, since get() is deprecated.
default: return QJsonValue(); | ||
} | ||
if (opt) | ||
return QJsonValue(*opt); |
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.
return *opt
?
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
} | ||
if (opt) | ||
return QJsonValue(*opt); | ||
return QJsonValue(); |
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.
personal preference:
if (!opt)
return QJsonValue();
return *opt;
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
} | ||
return ret; |
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.
ditch ret
and inline the returns.
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
src/gui/addnewtorrentdialog.cpp
Outdated
ui->startTorrentCheckBox->setChecked(true); | ||
else | ||
ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused()); | ||
ui->startTorrentCheckBox->setChecked(m_torrentParams.addPaused.get_value_or(!session->isAddTorrentPaused())); |
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.
wrong translation
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, thanks
else if (m_currentRule.addPaused() == TriStateBool::False) | ||
index = 2; | ||
if (m_currentRule.addPaused()) { | ||
if (m_currentRule.addPaused().get()) |
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.
use optional::operator==
:
if (m_currentRule.addPaused() == true)
index = 1;
else
index = 2;
at least std::optional works like it, not sure about boost:optional
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.
Unfortunately not supported
src/app/cmdoptions.cpp
Outdated
} | ||
else if (addPaused == TriStateBool::False) { | ||
result.append(QLatin1String("@addPaused=0")); | ||
if (addPaused) { |
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.
use operator==
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 no operator== (or any comparators) in boost's version of optional.
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.
According to this gotcha you can do:
if (addPaused == true)
result.append(QLatin1String("@addPaused=1"));
else if (addPaused == false)
result.append(QLatin1String("@addPaused=0"));
Its simpler. But will it confuse people? Maybe.
I don't have strong preference either way. Do you guys have any argument for or against one or the other?
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 prefer @sledgehammer999 variant.
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.
src/app/cmdoptions.cpp
Outdated
} | ||
else if (skipDialog == TriStateBool::False) { | ||
result.append(QLatin1String("@skipDialog=0")); | ||
if (skipDialog) { |
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.
use operator==
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
value() and value_or() require Boost 1.56. operator* is supported in 1.35, so that can be used in place of value(). The only difference is in exception handling. |
OK, you can squash the commits (I would squash all into the first one). |
@sledgehammer999 |
IIRC qBittorrent cannot use Boost 1.56 (it was stuck at 1.55 for a while), due to a change in Boost.asio. Perhaps use the first version to support the define workaround, instead? |
boost::optional (and std::optional in C++17) provide a more general mechanism for specifying tri-state values that works for types other than bool.
Done |
I don't remember that... or you might be talking about #4112? |
Replace use of TriStateBool with boost::optional.
@Chocobo1: No, I was referring to the issue that @sledgehammer999 solved using I do not recall if an alternative solution to this problem was discovered or not. If not, please make the minimum Boost version 1.58 rather than 1.56. |
That issue is still open: arvidn/libtorrent#314 (comment) Bumping to boost 1.58 which is released on 2015-04-17 (quite ancient) shouldn't pose a problem I guess. |
} | ||
return 0; // use default |
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 here you can safely do:
if (opt == true)
return 1; // always
else if (opt == false)
return 2; // never
else
return 0; // use default
A reader might scratch his head at first but afterwards he'll realize what we do here. No?
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 like the @sledgehammer999 variant (if it can be compiled) except the else
's after return
's.
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 @glassez means:
if (opt == true)
return 1; // always
else if (opt == false)
return 2; // never
return 0; // use default
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 (opt == true)
return 1; // always
if (opt == false)
return 2; // never
return 0; // use default
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
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.
Sorry I'm late to this, just want to point out it might be dangerous to use this form (in this specific case optional<bool>
), one might transform it to if (opt) return 1; if (!opt) return 2;
which is clearly not the same thing.
To avoid this pitfall, I personally like to see it as a container type, using operator*
to access it, which is also why I didn't think of using operator==
for comparing with values.
Just sharing my view point.
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 (opt) return 1;
if (!opt) return 2;
In my opinion, it looks "too ugly" to some sane developer want to write it like this.
In fact, this "extreme case" has forced me to abandon the use of any generic classes (boost::optional etc.). My TriStateBool doesn't allow any confusing implicit conversions.
But I don't want, of course, to prevent these changes because of their further spread to other fields. I just wish we have defined some common policy for its use.
I didn't think of using
operator==
for comparing with values.
But it's convenient, at least for types other than bool
. Otherwise boost::optional
becomes not much clear than using ordinary pointers.
Using if (optional)
to check for value existence is convient but it's confusing in case of bool
.
Btw, IMO we can safely bump Boost requirement to 1.56. The problem @LordNyriox mentions is valid only for Windows. And I am not going to provide Windows builds based on that old version anyway. I left 2 inline comments. They aren't merge-blocking. Since |
I need to make all add torrent params optional (for my further features) but I considered it in another way. I thought to implement AddTorrentParams as QVariantHash. But it also can be implemented as struct of optionals. @sledgehammer999, if you prefer this way, let me known and then I'll review this PR. |
FWIW, I already have the conversion of the rest of the struct to optionals done in another PR that I haven't sent for review yet. I was waiting for this one to get merged before sending it out. |
@glassez since its done already here there's probably no reason to do it via QVariantHash. |
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.
Also shouldn't we care about easy transition to std::optional? Maybe declare aliases for both boost::optional and boost::none?
#include "../utils/fs.h" | ||
#include "../utils/string.h" | ||
#include "rss_feed.h" | ||
#include "rss_article.h" | ||
|
||
namespace | ||
{ | ||
TriStateBool jsonValueToTriStateBool(const QJsonValue &jsonVal) | ||
boost::optional<bool> jsonValueToOptional(const QJsonValue &jsonVal) |
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.
jsonValueToOptionalBool or just jsonValueToBool
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 (and elsewhere)
} | ||
|
||
QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool) | ||
QJsonValue optionalToJsonValue(const boost::optional<bool> &opt) |
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.
optionalBoolToJsonValue or boolToJsonValue
} | ||
|
||
TriStateBool addPausedLegacyToTriStateBool(int val) | ||
boost::optional<bool> addPausedLegacyToOptional(int val) |
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 same as above
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.
addPausedLegacyToOptionalBool, as I said already.
} | ||
} | ||
|
||
int triStateBoolToAddPausedLegacy(const TriStateBool &triStateBool) | ||
int optionalToAddPausedLegacy(const boost::optional<bool> &opt) |
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 same as above.
if (opt) { | ||
if (*opt) | ||
return 1; // always | ||
else |
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.
In any case else
is redundant after return
.
} | ||
return 0; // use default |
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 like the @sledgehammer999 variant (if it can be compiled) except the else
's after return
's.
Sounds good, but once we replace it with std::optional we would continue using a "useless/extra" typedef/alias. So I am in favor of having before our eyes the real type*. |
I think it will probably be easier to just do a find & replace to swap types rather than having a header with aliases that needs to get included everywhere they are used and eventually deleted after the switch. |
@tgregerson: Any chance you can rebase / update this PR? It has been stagnant since February. |
@LordNyriox, please don't ask people to do potentially useless work. These changes are still not generally approved and may never be approved. But if we want to merge it, we'll ask to rebase the code in due time. |
I'll reintegrate if the powers that be can come to a consensus that we should move forward with it. As a side note, I would still like to implement the automatic category assignment feature I mentioned in the original comment on this PR, but I have not been able to make any progress since this first piece has been in limbo. |
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've just re-read this topic and re-review the code. In principle, this is not bad in itself (even if we do not look at other scheduled changes). Since I don't see any blocking objections from other project members I think we can go forward with it once it is rebased and fixed.
src/app/application.cpp
Outdated
@@ -426,7 +428,7 @@ void Application::processParams(const QStringList ¶ms) | |||
} | |||
|
|||
if (param.startsWith(QLatin1String("@addPaused="))) { | |||
torrentParams.addPaused = param.mid(11).toInt() ? TriStateBool::True : TriStateBool::False; | |||
torrentParams.addPaused = param.mid(11).toInt() != 0; |
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 wrap right part with parentheses.
And in assignment below too.
} | ||
|
||
TriStateBool addPausedLegacyToTriStateBool(int val) | ||
boost::optional<bool> addPausedLegacyToOptional(int val) |
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.
addPausedLegacyToOptionalBool, as I said already.
} | ||
if (opt == true) | ||
return 1; // always | ||
else if (opt == false) |
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.
Don't use else
after return
(as well as after break
, continue
etc.).
@@ -366,12 +366,10 @@ QStringList QBtCommandLineParameters::paramList() const | |||
if (!savePath.isEmpty()) | |||
result.append(QString("@savePath=%1").arg(savePath)); | |||
|
|||
if (addPaused == TriStateBool::True) { | |||
if (addPaused == 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.
IIRC, boost::optional<bool>::operator==
returns false
in both cases unless value is set, isn't it?
boost::optional (and std::optional in C++17) provide a more general mechanism for specifying tri-state values that works for types other than bool. Replace use of the newer "value" methods from boost::optional with the older "get" methods. These are mostly functionally equivalent, and are supported on older versions of Boost. boost::optional fixup optional cmdoptions fixup Address comments Revert "Merge branch 'autopopulate' into master" This reverts commit e2e6638, reversing changes made to 75e1ece.
I gave up on attempting to rebase. It would be easier to start from scratch than to try to resolve all the merge conflicts. |
Will you do so? |
boost::optional (and eventually std::optional in C++17) provide a generalized mechanism for specifying tristate variables of any type.
I discovered I needed tristate variables of different types than bool while scoping out the work for #5201 / #5779 so it made sense to do this cleanup first.