Cppcoreguidelines: The example in F.6 is a bad example of an exception that won't realistically be caught.

Created on 16 Jun 2019  Â·  5Comments  Â·  Source: isocpp/CppCoreGuidelines

The example in question is

vector<string> collect(istream& is) noexcept
{
    vector<string> res;
    for (string s; is >> s;)
        res.push_back(s);
    return res;
}

Which is justified being marked noexcept since most programs won't prepare to survive memory exhaustion.
I can easily imagine wanting to do something like

const vector<string> words{[]
{
    ifstream my_500mb_file{ "foo.txt" };
    try { return collect(my_500mb_file); }
    catch (const std::bad_alloc&)
    {
        cerr << "Not enough memory to read file\n";
        return vector<string>{};
    }
}()};

My suggestion:

static vector<function<void(const string&)>> output_event_handlers;

void register_output_event_handler(function<void(const string&)> handler) noexcept
{ output_event_handlers.push_back(handler); }

You could even use deque for that since its push_* usually only throws if there isn't a single byte left on your system*; at that point it's truly not possible to recover in almost every environment. Ofc there's probably going to be so few registered handlers that it doesn't matter.

*as opposed to vector which has to temporarily have an old and new block simultaneously, doubling-tripling the memory required.

Most helpful comment

The example says

  • You can use noexcept even on functions that can throw:
  • Unless the program is crafted to survive memory exhaustion, that may be just the right thing to do;

From my understanding the example just shows that even in cases where an exception can occur you can use noexcept. That might not be obvious for every C++ developer, so it is mentioned here.

It also says that this may be (not is!) the right thing to do if the program is known not to be crafted to survive memory exhaustion. In your case the initializer for words is crafted to survive memory exhaustion, so of course ("unless…") you would not add noexcept.

The example shows noexcept for throwing functions as an option, not as a good thing that you should do.

I agree with jwakely and don't see how your example is better than the given example. I believe that it is hard to come up with an example where everyone would agree that it is OK to always have the program be terminated. The point is that this strictly depends on the program and if it is crafted to survive memory exhaustion (or any other thrown exception) or not.

All 5 comments

But now you've turned an exceptional condition into an empty vector and a message on stderr. That requires the user of the vector to handle the error instead, and maybe nobody will ever see the stderr message.

Sure, you can handle out-of-memory conditions when you need to, nobody's saying you can't, but that's not the point of the example.

The user? I'm the one initializing words for use in my function which I will code to not have a "vector must contain something" invariant. cerr is just an example, maybe it would be a retry/cancel dialog box in a gui.

I can't handle the OOM error if it's noexcept that's the problem. This is a function that I could easily call in a way where I would expect it to fail a fair amount of the time, and I would often want to handle that failure. It shouldn't be marked noexcept.

The example says

  • You can use noexcept even on functions that can throw:
  • Unless the program is crafted to survive memory exhaustion, that may be just the right thing to do;

From my understanding the example just shows that even in cases where an exception can occur you can use noexcept. That might not be obvious for every C++ developer, so it is mentioned here.

It also says that this may be (not is!) the right thing to do if the program is known not to be crafted to survive memory exhaustion. In your case the initializer for words is crafted to survive memory exhaustion, so of course ("unless…") you would not add noexcept.

The example shows noexcept for throwing functions as an option, not as a good thing that you should do.

I agree with jwakely and don't see how your example is better than the given example. I believe that it is hard to come up with an example where everyone would agree that it is OK to always have the program be terminated. The point is that this strictly depends on the program and if it is crafted to survive memory exhaustion (or any other thrown exception) or not.

I can easily imagine wanting to do something like

if you want to be realistic, there will be 10+ stack frames between the function that asks the user for the file name and the function that OOMs while populating a data structure from the stream, which is how this happens in real apps that handle this kind of OOM, e.g. Notepad++.

but anyway, +1 to what @beinhaerter said above.

Editors call: We think the status quo example is what's wanted here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

galik picture galik  Â·  3Comments

franzhollerer picture franzhollerer  Â·  5Comments

larsch picture larsch  Â·  4Comments

HowardHinnant picture HowardHinnant  Â·  3Comments

Epholys picture Epholys  Â·  4Comments