Auto-throwing filesystem_error - Boost.Outcome documentation

# Auto-throwing filesystem_error

Something not mentioned at all until now (and properly described in the next section, Default actions) is that Outcome can be programmed take various actions when the user tries to observe `.value()` when there is no value, and so on for the other possible state observations.

Seeing as we are replacing the throwing overload of `copy_file()` in the Filesystem TS with a `result` returning edition instead, it would make sense if an attempt to observe the value of an unsuccessful `fs_result` threw the exact same `filesystem_error` as the Filesystem TS does.

Telling Outcome how to throw a `filesystem_error` with payload of the failing paths is easy:

    namespace filesystem2
    {
      // If we would like Outcome to do something other than the default action (see next
      // section), we can declare this ADL discovered free function to customise what
      // to do instead.
      //
      // Note that rvalue semantics are propagated internally by Outcome, so if the user
      // called .value() on a rvalued result, failure_info will be moved rather than
      // copied from the result. That means we can overload once with value semantics,
      // and not need to overload for lvalue and rvalue situations unless we really feel
      // we need to for some reason.
      inline void outcome_throw_as_system_error_with_payload(failure_info fi)
      {
        // If the error code is not filesystem related e.g. ENOMEM, throw that as a
        // standard STL exception.
        outcome::try_throw_std_exception_from_error(fi.ec);
        // Throw the exact same filesystem_error exception which the throwing copy_file()
        // edition does.
        throw filesystem_error(fi.ec.message(), std::move(fi.path1),  //
                               std::move(fi.path2), fi.ec);
      }
    }

View this code on Github

Reference documentation for the above functions:

- List of builtin `outcome_throw_as_system_error_with_payload()` overloads
- `void try_throw_std_exception_from_error(std::error_code ec, const std::string &msg = std::string{})`

Usage of our new “upgraded” Filesystem `copy_file()` might now be as follows:

      // Non-throwing use case
      auto o = filesystem2::copy_file("dontexist", "alsodontexist");
      if(!o)
      {
        std::cerr << "Copy file failed with error " << o.error().ec.message()                   //
                  << " (path1 = " << o.error().path1 << ", path2 = " << o.error().path2 << ")"  //
                  << std::endl;
      }

      // Throwing use case
      try
      {
        // Try to observe the successful value, thus triggering default actions which invokes
        // our outcome_throw_as_system_error_with_payload() above which then throws filesystem_error
        // exactly like the Filesystem TS does for its throwing overload.
        filesystem2::copy_file("dontexist", "alsodontexist").value();
      }
      catch(const filesystem2::filesystem_error &e)
      {
        std::cerr << "Copy file failed with exception " << e.what()                 //
                  << " (path1 = " << e.path1() << ", path2 = " << e.path2() << ")"  //
                  << std::endl;
      }
      catch(const std::exception &e)
      {
        std::cerr << "Copy file failed with exception " << e.what()  //
                  << std::endl;
      }

View this code on Github

Last revised: February 08, 2019 at 22:18:08 UTC