Comment by fluoridation

1 day ago

>If the API is that a function is infallible and then I decide that it’s a fallible function then that’s a pretty major change and I’m just gonna have to update all the call sites to deal with a fallible return result.

What if you don't control those call sites?

>Might as well just call std::abort.

Sure. I mean, not really, because the caller cannot handle an abort. You're making a decision for the caller that the situation is unresolvable, where the caller might disagree.

>No. You’ve simply made a new contract and the new contract is that under certain cases an error is returned in the form of an exception.

If the function doesn't use exceptions for normal error conditions, then no, it's not a new contract, because you don't need to do or know anything specific to handle the situation. You could do something like

  void transaction(){
    try{
      //...
      commit();
    }catch (std::exception &){
      rollback();
    }
  }

and not have to worry about the specifics. It's just an exception. You don't have to care about what exactly happened, you just care that something that couldn't be resolved happened. When exceptions are misused you see stuff like

  try{
    some_function();
  }catch (SomeErrorConditionSpecificToSomeFunction &){
    //...
  }

Not always, but this does usually mean that the exception is part of the contract of the function. It's a condition that the caller must handle as part of the normal usage of the function. FileNotFound exceptions are quite often a prime example of exception abuse.

Replying to your other comment here:

>They’re fallible functions. Don’t write fallible APIs that require exceptions to report errors! That’s bad API design!

I disagree. You should ensure your arguments are valid before indexing vectors and dereferencing optionals. You wouldn't iterate a vector like this, I imagine?

  for (size_t i = 0;; i++){
    auto x = vector.at(i);
    if (!x.has_value())
      break;
    //...
  }

> What if you don't control those call sites?

If I am choosing to change the API contract then someone who wants to use the new API has to update. This is not a big deal.

> If the function doesn't use exceptions for normal error conditions, then no, it's not a new contract

I disrespectfully and emphatically disagree. I do not accept your definition of contract.

> You could do something like (try-catch wrapper)

Let me be clear. Having to add a bunch of random fucking try-catch bullshit around every fucking function call is EXACTLY why I hate exceptions and is EXACTLY what I think is bad software design.

If you think a function should return a value or some unspecified exception whose details are irrelevant then that function could return an option with no information loss, or a result with an Error that is ignored.

> You wouldn't iterate a vector like this, I imagine?

I wouldn’t use at(i) for iteration. The only reason for a function like at(i) to exist is because you want it to be fallible.

  • > Let me be clear. Having to add a bunch of random fucking try-catch bullshit around every fucking function call is EXACTLY why I hate exceptions and is EXACTLY what I think is bad software design.

    The whole point of exceptions is that you don’t need to handle errors at every call site. You can just have one central try-catch block at a place where you have a way to deal with the error, such as report it to the user.

  • > having to add a bunch of random fucking try-catch bullshit around every function call

    Not the person you are replying to, but did you see where he said:

    > When exceptions are misused

  • >If I am choosing to change the API contract then someone who wants to use the new API has to update. This is not a big deal.

    Throwing lets you handle the new situation without changing the API at all.

    >Let me be clear. Having to add a bunch of random fucking try-catch bullshit around every fucking function call is EXACTLY why I hate exceptions and is EXACTLY what I think is bad software design.

    See, that's what happens when you form your opinions on half-digested ideas. Let me be clear. You don't add "a bunch" of try-catch blocks. You don't wrap every call that's capable of failing exceptionally in a try-catch block. That's exactly how you don't use exceptions. The whole point of exceptions is that the compiler will handle the stack unwinding for you so you don't need to worry about it. If you don't want to, or don't know how, or can't handle an exception at a specific point then don't. Let it bubble up for someone else to catch. See the ellipsis in my example? Inside of it you might have a gigantic call tree that performs all sorts of different operations that may all fail in different and unexpected ways. You could write the whole thing and not have a single try-catch besides the one I wrote explicitly. Let me reiterate; this is what you DON'T do:

      try{
        foo();
      }catch (...){
        return Error1;
      }
      try{
        bar();
      }catch (...){
        return Error2;
      }
      try{
        baz();
      }catch (...){
        return Error3;
      }
    

    The only reason you would do something like this is to satisfy a specification such that you have to return different errors, specifically when each of the different calls fails. So... don't specify your functions such that you're required to do this? Just do

      foo();
      bar();
      baz();
    

    or if you really must not throw from the function,

      try{
        foo();
        bar();
        baz();
      }catch (...){
        return SomethingFailed;
      }
    

    TL;DR: Instead of bitching about exceptions, learn how to use them properly.

    • > Throwing lets you handle the new situation without changing the API at all.

      I do not disagree. https://xkcd.com/1172/

      If you add exceptions to a library that didn’t previously use them then I almost definitely have to update my code. The fact that it compiles and runs but will behave in undesirable ways makes it even worse, not better!

      > or if you really must not throw from the function,

      I’m aware.

      But if your library that offers foo adds exceptions now I need to think about it at every single callsite, and probably wrap the function. It’s extremely irritating.

      > learn how to use them properly.

      In my 20+ years of professional C++ development I have a great experience not using exceptions and a strictly negative experience using them.

      Perhaps sometimes I’ll stumble upon a library or codebase where exceptions make the code simpler, easier to understand, and easier to write. But my experience is exceptions make everything strictly worse and that not using exception is a strict win with zero downsides.

      18 replies →