3

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?

Chris Beck
  • 15,614
  • 4
  • 51
  • 87

4 Answers4

2

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...

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
2

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.

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

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())) {
srdjan.veljkovic
  • 2,468
  • 16
  • 24
  • You are on the right track, but this is not a drop-in replacement for std::optional, since you are appropriating a valid value of T. – Dúthomhas Jul 17 '16 at 19:44
0

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.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39