Is is possible in C++ to throw nothing?
Asked Answered
S

5

6

Under exceptional circumstances, I want my program to stop processing, output an error to std::cerr, clean up, and exit.

However, calling exit() will not call all the destructors of any objects that have been constructed. I would like to have the destructors called nicely, so I wrapped all the code in a try-catch block, something like this:

int main(int argc, char** argv){
    try {
        bool something_is_not_right = false;
        /* lots of variables declared here */
        /* some code that might set something_is_not_right to true goes here */
        if(something_is_not_right){
            std::cerr << "something is not right!!!" << std::endl;
            throw '\0';  // dummy unused variable for throwing.
        }
    }
    catch (...) {}
    return 0;
}

In this way, I get guaranteed destruction of all my variables. But I can't seem to find a way to get C++ to throw nothing. throw; has a special meaning in C++; it isn't throwing nothing.

Is there a way to throw nothing?

Selinski answered 28/4, 2017 at 8:35 Comment(13)
Why do you need to throw nothing, instead of throwing your custom type abnormal_program_termination?Train
I agree with @SebastianRedl. See cplusplus.com/doc/tutorial/exceptions and en.cppreference.com/w/cpp/language/throw ... Throw is not intended to be used without an expression, i.e. something you can throw around. You can solidify your code, using custom errors, exceptions or other error-objects...Herries
If you want full cleanup, then calling exit() might not be the right thing to do in the first place.Franco
@SebastianRedl So it seems the solution is to make something out of nothing.Selinski
You only use throw; without anything if you handled an exception in a catch block and want to rethrow to the caller.Moberg
@HenriMenke Unless nothing is being handled, then std::terminate is called.Syndic
@Selinski Or the solution is to stop thinking about it as throwing nothing, and instead thinking about it as throwing an abnormal program termination error. If it was nothing, you wouldn't need to throw.Train
in a very simple (non-recursive) case, simple return 0 will also call all destructors. If you want the other, just create your own exception class to be handled as termination by abnormal condition.Mikvah
Instead of setting something_is_not_right = true, why not throw something_is_not_right?Franco
@Snps It feels redundant because something_is_not_right is always false when thrown.Selinski
@Selinski Yes but something_is_not_right can instead be a struct something_is_not_right : public std::runtime_error { using std::runtime_error::runtime_error; }. The point is that you already have some logic that handles an error using a boolean variable. Why not replace setting a bool to true with throwing an exception? You've already succumbed to using exceptions anyway.Franco
@Snps the bool was just a simple way to illustrate my predicament.Selinski
I think the key insight to this question is this comment by @SebastianRedlSelinski
C
12

No

It's not possible to throw nothing. You need to throw something. While you may have seen people use the throw keyword without anything, this just means they are re-throwing the currently handled exception.

Confidence answered 28/4, 2017 at 8:52 Comment(1)
As mentioned in https://mcmap.net/q/1586333/-is-is-possible-in-c-to-throw-nothing, if you do attempt to throw nothing it will compile but will terminate the program without any stack unwinding being performed.Copra
M
8

This is not a direct answer to your question, but a personal recommendation.

You might want to take a look at the predefined exceptions of stdexcept which cover almost any exceptional behaviour that occurs in a program. In your case I would throw a std::runtime_error. Also, only catch what you expect to be thrown and not catch 'em all. If you really want to catch everything, then catch std::exception (the base class of all standard exceptions).

In my opinion handling unknown exceptions doesn't really make sense and the only logical consequence is to just abort execution.

#include <iostream>
#include <stdexcept>

int main()
{
  try
  {
    bool something_is_not_right = true;
    if ( something_is_not_right )
      throw std::runtime_error("something is not right!!!");
  }
  catch (std::runtime_error& e)
  {
    std::cerr << e.what() << '\n';
    throw;
  }
}
Moberg answered 28/4, 2017 at 8:58 Comment(5)
This is a view that I have not heard of before. While throwing a runtime_error would be okay in this question, there might be other things (with additional properties perhaps) that one might need to throw, no?Selinski
@Selinski I think I don't understand your question. Of course, if you have exceptional behaviour which is not covered by or needs extensions to the standard exceptions like std::runtime_error, then you define your own.Moberg
I think I have misunderstood your answer and taken "unknown exceptions doesn't really make sense" to mean "defining your own exceptions doesn't really make sense".Selinski
@Selinski Ah, I see. Yes, only handle what you expect and what you know. If you do not expect std::logic_error but only std::invalid_argument, then you only handle std::invalid_argument and leave std::logic_error unhandled.Moberg
I think this is the key, only catch what you can handle. Catching everything is generally (not always) a bad thing. My goto example for someone catching everything is "how are you going to handle an out of memory exception". When they say "it's just for logging", my response is "you are out of memory, why do you think the logger will even work in that situation".Mutism
S
3

Well you "can" but it doesn't throw nothing. It terminates.

5.17 Throwing an exception:

  1. Evaluating a throw-expression with an operand throws an exception (15.1)
  2. A throw-expression with no operand rethrows the currently handled exception (15.3).
  3. If no exception is presently being handled, evaluating a throw-expression with no operand calls std::terminate()

This is valid:

int main () {
  throw;
  return 0;
}

Source used: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

But it won't cleanup anything. std::terminate is used when the cleanups fail.

Otherwise you have to use operand, and then this section becomes relevant:

15.1 Throwing an exception [except.throw]

  1. Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable declared in the matching handler (15.3).

So you have to pass something that is able to be initialized, which by definition cannot be nothing.

Syndic answered 28/4, 2017 at 8:50 Comment(9)
Only because it is valid does not mean you should do it. Also the return 0; is gratuitous, as the standard guarantees that a return 0; is implicitly inserted at the end of main.Moberg
This is not useful. The OP wants to know whether it's possible to unwind without an exception object. But "throwing nothing" (a simple throw;) looks for a currently-caught exception object and throws that; it doesn't throw nothing.Train
@HenriMenke And since it is unreachable code, the compiler may even warn about it.Train
"evaluating a throw-expression with no operand calls std::terminate()" - the default std::terminate_handler will call std::abort which will not cleanup anything.Franco
I have added the information that throwing nothing does not perform cleanups. And changed the wording to be clearer.Syndic
@Selinski It may not solve your problem (which seems to be stubbornly not wanting to throw something when you should make something to throw or at least just throw std::runtime_error), but it does provide information of what actually happens if you throw; "nothing", so it is certainly part of an answer to your question.Copra
@Selinski It kind of does IMHO. Because if you can't throw "nothing", you have to throw something. It cannot be "void". I have added another reference.Syndic
@Syndic Seems like essentially C++ forbids throwing nothing by design.Selinski
@Selinski Well, there is a syntax for "nothing", but it is used to call a rather abrupt form of termination. And the other one specifically states, there is a temporary object, and that it is an lvalue, which are pretty strongly constituted entities, if one might say so. But I don't see nothing wrong in using some standard exception like std::runtime_error, I think closest you can get to nothing is to define an empty class and throw an instance of it. But even then being pedantic it has to have storage, and its sizeof is non zero.Syndic
M
3

How do you know that the reason for the catch is your error or something else (out of memory is always a good one). If you detect an error, then you should create and throw the reason for that error in a custom exception. Then in main, you can tell the difference between your detected error and something you didn't expect. It's just good practice.

Mutism answered 28/4, 2017 at 8:51 Comment(0)
R
2

In order to ensure full cleanup you have to throw an exception and catch it somewhere. Implementations are not required to clean up stack objects when an exception is thrown but not caught. The requirement (in [except.handle]/9) when an exception is thrown but not caught is that the program calls std::terminate(), and it's implementation-defined whether stack objects are cleaned up.

Richierichlad answered 28/4, 2017 at 11:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.