lefticus / cpp_weekly

The official C++ Weekly Repository. Code samples and notes of future / past episodes will land here at various times. PR's will be accepted in some cases.
The Unlicense
663 stars 24 forks source link

Nested exceptions; value and best practices #363

Open cwsamuelson opened 5 months ago

cwsamuelson commented 5 months ago

Channel

C++ Weekly

Topics

In general, most people seem unfamiliar, so the broad topic should be: What are nested exceptions, what is their value, and what is the best way to use them. Since I'm already familiar, what I would be interested in is best practices, or trade-offs/costs to using them (and maybe how they work/are implemented).

Since I already use them, I'll provide an example: Your typical use case for exceptions ends up being

void throwingFunction();

int main() {
  try {
    throwingFunction()
  } catch (const std::exception& e) {
    log(e.what());
    // exit, with minimal, possibly cryptic error message
  }
}

Since I have a support library to help me use nested exceptions easily, my code looks like:

void throwingFunction(int x);
void functionA() {
  try {
    int x = /**/;
    throwingFunction(x);
  } CATCH_AND_NEST("Here's some extra information about what happened: {}", x)
}

int main() {
  try {
    functionA();
  } CATCH_AND_DECOMPRESS// provides a more complete log about what happened
}

In this example, CATCH_AND_NEST will catch the exception, nest it into a new exception, and format the message to put into that new exception and throw it. Since you can nest a nested_exception into a nested_exception, CATCH_AND_DECOMPRESS goes through all the nested exceptions, extracts their messages, and formulates a more complete error message. If necessary I can provide the content of those macros for a more complete picture (unfortunately CATCH_AND_DECOMPRESS ends up being particularly nasty).

I use this to get error information from across the call stack about what was happening when the error occurred, not just the message exactly at the site the error initially occurred. I haven't been doing this long, but I find it can provide useful information about what was happening when the error occurred, and not just a single error message nested deeply in the call stack with no way to figure out why this happened.

Since I don't see anybody else doing this, I wonder if I'm missing something about it; is there a hidden cost? Is it not providing me as much value as I think it is?

I also wonder if my usage is 'optimal'; is there a better way to use them, or maybe I'm actually entirely abusing them.

Curious about what you may know, or what your thoughts about them are.

Length Longer form; I think there's enough to say about it to cover at least 10-15 minutes.

LB-- commented 3 months ago

I'm not sure why you need the CATCH_AND_NEST macro when it barely replaces any code at all, it effectively boils down to catch(...){ std::throw_with_nested(std::runtime_error(std::format("message: {}", x))); } which I think is more clear about how things work and doesn't leave you guessing what exception types are involved. If you want std::source_location then you would just replace std::runtime_error with your own exception type (possibly derived from it) that takes a defaulted second parameter to autofill the source location information.

cwsamuelson commented 2 months ago

Sure, that's what the macro does, I find the shorthand easier to read, since there's less boilerplate involved. It's also easier to understand for developers who don't understand the mechanism at all.

Besides, I'm not really worried about implementation details for a video, so much as whether I'm barking up the wrong tree as a whole, or have a bad approach. If people think it's a good idea, then I'll be worried about the implementation more.