Boost logo

Boost :

Subject: Re: [boost] [review] Review of Outcome v2 (Fri-19-Jan to Sun-28-Jan, 2018)
From: Vinícius dos Santos Oliveira (vini.ipsmaker_at_[hidden])
Date: 2018-01-28 20:10:27


2018-01-28 12:12 GMT-03:00 degski via Boost <boost_at_[hidden]>:

> 1. Use of outcome is more manual and cumbersome, i.e. more error-prone.
>

What is complex about the following?

outcome::result<int> convert(const std::string& str) noexcept
{
  if (str.empty())
    return ConversionErrc::EmptyString;

  if (!std::all_of(str.begin(), str.end(), ::isdigit))
    return ConversionErrc::IllegalChar;

  if (str.length() > 9)
    return ConversionErrc::TooLong;

  return atoi(str.c_str());
}

Or maybe about the following?

outcome::result<void> print_half(const std::string& text)
{
  if (outcome::result<int> r = convert(text)) // #1
  {
    std::cout << (r.value() / 2) << std::endl; // #2
  }
  else
  {
    if (r.error() == ConversionErrc::TooLong) // #3
    {
      OUTCOME_TRY (i, BigInt::fromString(text)); // #4
      std::cout << i.half() << std::endl;
    }
    else
    {
      return r.as_failure(); // #5
    }
  }
  return outcome::success(); // #6
}

Let's convert the first example to exceptions and see how much more
readable your exceptions are:

int convert(const std::string& str)
{
  if (str.empty())
    throw ConversionErrc::EmptyString;

  if (!std::all_of(str.begin(), str.end(), ::isdigit))
    throw ConversionErrc::IllegalChar;

  if (str.length() > 9)
    throw ConversionErrc::TooLong;

  return atoi(str.c_str());
}

It looks the same to me. Let's try to convert the second example:

void print_half(const std::string& text)
{
  try
  {
    int r = convert(text);
    std::cout << (r / 2) << std::endl;
  }
  catch (const ConversionErrc &e)
  {
    if (e == ConversionErrc::TooLong)
    {
      BigInt i = BigInt::fromString(text);
      std::cout << i.half() << std::endl;
    }
    else
    {
      throw;
    }
  }
}

Also looks the same. So... you give us an example where exceptions are
specially less verbose (and I'm not challenging that your case is uncommon
because it looks very common to me), but then you generalize stating that
it'll be like this everywhere. What is that old ditto? You can write shitty
code in any language? It sounds to apply here. Just because you can write
shitty code with Boost.Outcome it doesn't mean that Boost.Outcome is shit.

Failure (when exceptions would be thrown) will in general be the result of
> lack of resources (network-connection(s), memory, disk-space). The handling
> of lack of resources (as I see it) would normally just try to stop the app
> from doing more damage and to preserve a state (of data etc...), that is
> hopefully valid. Logic errors just need fixing in my view, not exception
> handled.
>

Server side code is expected to handle lot of failures and none of them are
"stop the world". Also, if you add significant cost to the failure case,
you can risk easy DoS on your app.

If the error is exceptional or not, it depends on context, not on the
algorithm (e.g. connection failure on game client code and game server code
and both of them being backed up by the same functions). Boost.Outcome
let's you do just that by converting an error into an exception.

-- 
Vinícius dos Santos Oliveira
https://vinipsmaker.github.io/

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk