3

I went through several discussion, tutorials etc. and I've a feeling like there is no way to inform the user using prototype, that his function might throw an exception.

For instance:

/* AudioStream.h */
class AudioStream
{
   int open(struct stream_settings &settings);
}
/* AudioStream.cpp */
int AudioStream::open(struct stream_settings &settings)
{
    int err;
    err = snd_pcm_open(...);
    if (err < 0)
    {
        /* Throw some exception here */
    }
}

If the final product ends up in a library with a header. How does one figures out, that the open function throws an exception and it's necessary to put it into a try/catch block?

Thank you for all the great answers.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
ST Renegade
  • 311
  • 3
  • 14
  • You are right, there is no language feature to do this. That's what documentation is for. – Lukas-T Feb 13 '20 at 20:34
  • Consider `void Foo();` may throw in some situations, or may never ever throw no matter what. Can't be sure. Whereas `void Bar() noexcept;` won't throw no matter what, upon pain of terminating the process. – Eljay Feb 13 '20 at 20:39
  • 2
    All functions* are potentially throwing unless marked `noexcept` (*some functions are implicitly `noexcept`) – AndyG Feb 13 '20 at 20:40
  • 1
    It's important to remember that anything that does a `new` can end up throwing a `std::bad_alloc`. So you should be treating most functions as being capable of throwing in the first place. RAII should cover most cases though. –  Feb 13 '20 at 20:42
  • Of course, just because a function might throw an exception doesn't mean that it's *necessary* to put it in a `try/catch` block - only if you intend to do something interesting in case it does throw. – Nate Eldredge Feb 13 '20 at 20:42
  • So in C++ one should automatically assume, that a function can throw an exception? That sounds very strange to me. – ST Renegade Feb 13 '20 at 20:43
  • @STRenegade a better way to put it is that you should strive to write code that does not care wether a function throws an exception or not. It's a big part of why `std::unique_ptr<>` is prefered over new/delete. –  Feb 13 '20 at 20:45
  • @STRenegade Yes, you need to assume everything might throw unless it's `noexcept`. This is why RAII is extremely important do understand and use at all time. If you make sure to store resources in exception safe handlers, then you solve most of the complexity related anything being able to throw. – François Andrieux Feb 13 '20 at 20:46
  • Unless you see `noexcept` or `noexcept(true)` or `noexcept(some_expression_you_know_to_be_true)` assume the function can throw. – NathanOliver Feb 13 '20 at 20:46
  • *"How does one figures out, that the open function throws an exception and it's necessary to put it into a try/catch block?"* There is a common misconception that you should put functions that can throw in a `try` block. You should only use `try` blocks where you an reasonably and meaningfully `catch` and handle possible exceptions. If you can't do anything with an exception, don't catch it. – François Andrieux Feb 13 '20 at 21:15
  • The latest version of c++ in 2019 was c++17. I'll tag and edit the question accordingly. It's enough to have the tag, I've removed it from the title. – François Andrieux Feb 13 '20 at 21:18
  • @Francois Andrieux As I was not able to find anything more, I thought that the concept was the same/similar to Java, where you have to try/catch everything that throws an error, if I remember well. – ST Renegade Feb 13 '20 at 21:19
  • In Java (in my limited experience) you have the obligation to `catch` any number of possible exception a piece of code might throw and then mark the function as throwing any other exception the function doesn't catch. This is because the compiler enforces exception specifications at compile time, but this is not technically possible in c++. – François Andrieux Feb 13 '20 at 21:21

4 Answers4

2

Unlike some languages which indicate in the function signature that it "throws", C++ has no such mechanism. This is something you must establish in the documentation or comments near the function definition.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thanks a lot for the clarification. I was not sure, whether I've missed something or C++ really lacks this. So using an empty macro like: #define THROWS_EXCEPTION and defining the function like: void my_function() THROWS_EXCEPTION; should work... or would you discourage me from such an approach? – ST Renegade Feb 13 '20 at 20:40
  • 1
    If you do such a thing it's going to mark you as someone who's trying to force C++ to be something it's not, it's a form of programming with an accent. Any veteran C++ programmer knows that any given function *could* throw exceptions and plans accordingly. It's expected that the documentation explains in great detail how the function should be used, and what exceptions can be thrown. As a random example, [`from_bytes`](https://en.cppreference.com/w/cpp/locale/wstring_convert/from_bytes) explains what it returns and what it might throw. – tadman Feb 13 '20 at 20:42
  • 3
    @tadman This answer should account for `noexcept`. C++ has a mechanism for indicating that a function throws, and that is by it not being `noexcept` (the default case). – François Andrieux Feb 13 '20 at 20:43
  • The more you try and wallpaper over language features you don't like, the more you're going to write code another C++ programmer can't make sense of. Since `THROWS_EXCEPTION` is meaningless to the compiler and only relevant to developers it would be better to add a comment above the definition that indicates which exceptions to expect. This is especially relevant in header files where the implementation details are not shown and it's not obvious what to expect. – tadman Feb 13 '20 at 20:44
  • @FrançoisAndrieux A very good point about the inverse case where you promise to not emit exceptions. I just haven't seen a lot of that in the wild, but for those who want to embrace that aspect then sure. That's from C++11 which is well-established by now, but it's [not without controversy](https://stackoverflow.com/questions/10787766/when-should-i-really-use-noexcept). – tadman Feb 13 '20 at 20:45
  • 4
    @tadman `noexcept` is actually very common in any sort of `swap` function, destructors, move constructors and move assignment operators. This is essential for exception safety. Other than those common cases, using `noexcept` should probably be avoided unless you're sure about it because it's an irrevocable API promise. – François Andrieux Feb 13 '20 at 20:47
2

Like already said, you cannot necessarily determine if a function may throw or not. The opposite however, is indeed possible. By declaring a function noexcept you have the guarantee that this function will never throw an exception that leaves the own function body. However, if a noexcept function were to throw anyway without handling the exception in itself, the program will be terminated. So as long as a function isn't declared noexcept, and you have no documentation or knowledge about the function, expect that it may throw exceptions.

Another possibility is the use of error codes or std::optional instead of throwing exceptions.

There is also a great talk from my man Herb Sutter about exceptions.

And here are some examples how you could avoid exceptions in favor of some sort of error codes.

Timo
  • 9,269
  • 2
  • 28
  • 58
  • That's what I use in C, usually I return an error code. As I'm writing an app in C++ now, I thought that exceptions "replaced" this mechanism from several reason. But now I'm confused a bit. Need to digest this now... :-) – ST Renegade Feb 13 '20 at 20:50
  • @STRenegade C++ gives you a multitute of possibilities to handle errors in your code. They all have advantages and disadvantages. In the end it is your personal preference that matters (except if you do some high performance computing). Also, note that error codes aren't necessarily simple integers (like in C). – Timo Feb 13 '20 at 20:52
  • As I'm from C world writing a C++ app, I wanted to make sure I'm writing the code based on best practices or good habits. But apparently, there are several ways to go. – ST Renegade Feb 13 '20 at 20:54
  • @STRenegade This is one of the most jarring differences when coming to c++ from c. Most function call and many operators can be an invisible exit point in a function. This seems very brittle and dangerous at first, and is one of the main driving forces to many of c++'s best practices. It takes a while to get used to it, but once you get comfortable with it, it is actually quite elegant. Error handling is hidden from the general logic path and the core logic of the code is much more readable and it makes it difficult to accidentally forget to handle errors. – François Andrieux Feb 13 '20 at 20:55
  • 1
    @STRenegade Whatever you do, **don't** use exceptions for control flow. Exceptions are for something that is exceptional and have a high cost when thrown. For stuff like bad input from a user or division by zero, you should use if statements, error codes and other techniques to handle that. – NathanOliver Feb 13 '20 at 20:57
  • @STRenegade Keep in mind that exceptions should be reserved for truly exceptional situations. If searching a container is expected to frequently not find an element, failure to find should not be an exception. An alternative error reporting solution should be used. If you always expect to find the element, failure to find should be an exception. Something unexpected went wrong. – François Andrieux Feb 13 '20 at 20:57
  • @Francois Andrieux, Timo Those are actually really good points and hints. I thought of exceptions as a "replacement" for error codes. – ST Renegade Feb 13 '20 at 21:01
  • @Timo Perfect, I'll definitely check them to better understand the nuances between different techniques – ST Renegade Feb 13 '20 at 21:25
1

You can use noexcept(false) to explicitly specify that a function can throw. This does not add any information for the compiler, since can throw is the implicit default for every function without an noexcept specification.

So the recommended way would probably be to add a noexcept specifier to each function that cannot throw.

See also https://en.cppreference.com/w/cpp/language/noexcept_spec and Does adding `noexcept(false)` benefit the code in any way?

smerlin
  • 6,446
  • 3
  • 35
  • 58
1

C++ had throw specification that is deprecated since C++ 11 and could be used like this:

void f() throw(int);

but the absence of this specifier in the function prototype did NOT guarantee that function won't throw

Starting from C++ 11 there is noexcept specifier:

void f() noexcept; // the function f() does not throw
void (*fp)() noexcept(false); // fp points to a function that may throw

but again

noexcept specification on a function is not a compile-time check; it is merely a method for a programmer to inform the compiler whether or not a function should throw exceptions

The above code samples are taken from linked pages and you can read more about these specifiers there.

UPDATE: Following @FrançoisAndrieux suggestion I want to add that although it is not compile-time check you can check at compile time whether a function has this specifier or not, which might help you to optimize your code

mvidelgauz
  • 2,176
  • 1
  • 16
  • 23