Reputation: 16204
When using C-style return codes to signal errors, it's pretty common to see code like this:
if (do_something()) {
do_something_else();
} else {
report_failure();
}
Sometimes, if one block is much larger than the other, you might want to reorder the "handle failure" block before the "do_something_else" block.
if (!do_something()) {
report_failure();
} else {
do_something_else();
}
(Or, when it really is C-code the codes may be such that 0
indicates success rather than failure, but let's ignore that.)
When I use C++ idioms like boost::optional
or one of the proposed std::expected
types, usually what I want to do is put a declaration inside the condition of the if statement:
if (auto ok = do_something()) {
do_something_else(*ok);
} else {
report_failure(ok.error());
}
I like to do this because this way, ok
is strictly contained in scope, it's not visible outside the two blocks.
However, once I do it this way, I can't reorder the two branches if I want, which probably annoys me much more than it should, but still.
What I would really like is a syntax
if not (auto ok = do_something()) {
report_failure(ok.error());
} else {
do_something_else(*ok);
}
But to my knowledge that doesn't actually work.
Is there a trick to accomplish that?
Upvotes: 3
Views: 109
Reputation: 2548
Well, a lot of dirty tricks come to mind involving macros, but, supposing you don't want to go there, here's a non-macro trick:
template <class T> class notter {
T d_t;
public:
notter(T &t) : d_t(t) {}
notter(T t) : d_t(t) {}
operator bool() { return !d_t; }
T &data() { return d_t; }
};
Now you can use it as:
if (notter<int> a = do_something()) {
report_failure();
}
else {
do_something_else(a.data());
}
This assumes that do_something
returns an int
. You may avoid naming the type with decltype
like this:
if (notter<decltype(do_something())> a = do_something()) {
but in cases like this, that may be overkill.
You may tweak it to your needs, if, say, data()
is too verbose for you, or you want just one of the constructors, or to make a more "drop-in replacement" for optional<>
(as per comments from Duthomhas) or expected<>
- you may employ template specialization.
Also, you can take hint from std::make_shared()
and such:
template<class T> notter<T> make_notter(T t) { return notter<T>(t); }
and use it like:
if (auto a = make_notter(do_something())) {
Upvotes: 0
Reputation: 10048
Alright, so here's a little class that does what you want. Dress it up however you like.
template <typename T>
struct not_optional_type: public optional <T>
{
typedef optional <T> base_type;
not_optional_type( const base_type& v ): base_type( v ) { }
operator bool () const { return !(base_type)(*this); }
T operator * () const { return *(base_type)(*this); }
};
template <typename T>
not_optional_type <T>
not_optional( const optional <T> && v )
{
return not_optional_type <T> ( v );
}
Use it as you would expect:
if (auto ok = not_optional( do_something() ))
fooey();
else
success( *ok );
I personally think the proposed if
syntax modification is an abomination.
Upvotes: 0
Reputation: 275385
C++17 will introduce this syntax:
if (auto ok = do_something(); !ok) {
report_failure(ok.error());
} else {
do_something_else(*ok);
}
Which is basically what you want.
It is in the feature-complete draft.
Upvotes: 2
Reputation: 96258
You can add an extra scope:
{
auto ok = do_something();
if (! ok) {
report_failure(ok.error());
} else {
do_something_else(*ok);
}
}
Personally I wouldn't add those braces as the scope should be clear from the rest of the code, if you have too much functionality in one function you should refactor the code anyways...
Upvotes: 2