How to catch exception from constructor without handling entire the function?

Viewed 80

This question is all about just finding any solution that doesn't carry too much boilerplate code - while this could generate some opinion based solutions - Im really just looking for any solution (wihtout too much boilerplate code)

The following is causing me a bit of headache - while I can easily fix this, I would like to avoid too much boilerplate code. The following is not legal but shows intent.

char* newBuffer(const char* filename)
{
    auto fm = try { //<--try is not legal here
        boost::interprocess::file_mapping(filename, boost::interprocess::read_only);
    }
    catch (boost::interprocess::interprocess__exception& e) {
        //report error
        return nullptr;//<--this actually makes matters worse since now we cannot just simply wrap in a simpel lambda or function..
    }
    //rest of function ...
}

snippet on godbolt

I could just move the try at the beginning of the function but that would mean to just try the entire function (bad practice) instead of just try'ing file_mapping constructor. How would you solve this conundrum and avoid try-block around the entire function ? less boilerplate code is preferred better.

Any answer should also address the issue with returning of nullptr in case of failure.

4 Answers

Normally you'd declare the variable outside the try block:

boost::interprocess::file_mapping fm;
try {
  fm = boost::interprocess::file_mapping(filename, boost::interprocess::read_only);
} catch (boost::interprocess::interprocess__exception& e) {
// Handle error  
  return nullptr;

}

This requires the type to be movable at least.

You could return it from a lambda, but then you'd have to return a std::optional or similar to indicate the error case, or throw another exception.

You can use a directly invoked lambda, like so:

auto fm = [&] { try {      
        return boost::interprocess::file_mapping(filename, boost::interprocess::read_only);
    } catch (const boost::interprocess::interprocess__exception& e) {
    //report error
    } }();
// Rest of function

This is very much similar to Jesper Juhl's answer and his comment, but I have clarified how to deal with returning a nullptr should the constructor throw.

char* newBuffer(const char* filename)
{
    auto fm_opt = [&]{
        try {
            return std::make_optional(boost::interprocess::file_mapping(filename, boost::interprocess::read_only));
        }
        catch (boost::interprocess::interprocess__exception& e) {
            //report error
            return std::nullopt;
        }
    }();

    if(!fm_opt.has_value()) return nullptr;

    //rest of function ...
}

Controlling constructor invocation timing is a feature of std::optional (beyond the obvious “represent a lack of object”):

char* newBuffer(const char *f) {
  std::optional<boost::interprocess::file_mapping> fm0;
  try {
    fm0.emplace(f,boost::interprocess::read_only);
  }
  catch (boost::interprocess::interprocess_exception& e) {
    //report error
    return nullptr;
  }

  auto &fm=*fm0;
  // …
}

There is the minor overhead involved of storing the “non-empty” state of the optional, but that’s probably of no concern.

Related