0

Say I have this code:

class Foo {
public:
    Foo() {};
    ~Foo() {
        // Some code
        if (error_that_should_never_ever_happen)
            throw SomeException("Some error message");
        // Some code
    }
};

In c++11 and above destructors have noexcept(true) so if error_that_should_never_ever_happen does happen SomeException couldn't be caught and the program would be terminated because of uncaught exception which is great because that's what I want (if error_that_should_never_ever_happen does happen then it's really bad).

But I want to test the code so I have this test:

Foo* f = new Foo();
try {
    // Some alien code that will create a error_that_should_never_ever_happen in ~Foo()
    delete f;
    assert(false);
} catch(SomeException& ex) {
    assert(true);
}

What is the best thing to do:

  1. To remove both the if(error_that_should_never_ever_happen) and the test for it which will create undefined behavior if error_that_should_never_ever_happen.
  2. To remove only the test so I would have untested code (why test something that should never ever happen)
  3. To declare the destructor with noexcept(false) which will create problems if the code is reused by somebody else and the exception is caught.
  4. To compile the application with a flag -DTEST_ENABLED if I also compile the test (which I allready do) and to make Foo look like this:

    #ifndef TEST_ENABLED
    #define FAIL_SAFE_FOO_DESTRUCTOR true
    #else
    #define FAIL_SAFE_FOO_DESTRUCTOR false
    #endif // TEST_ENABLED
    
    
    class Foo {
    public:
        Foo() {};
        ~Foo() noexcept(FAIL_SAFE_FOO_DESTRUCTOR) {
            // Some code
            if (error_that_should_never_ever_happen)
                throw SomeException("Some error message");
            // Some code
        } 
    };
    

which will make the code less readable and less portable.

I'm open for more elegant solutions.

Pinte Laurentiu
  • 43
  • 3
  • 12
  • Mind that in c++ destructors should never throw exceptions. – Emerald Weapon Oct 22 '17 at 13:39
  • 2
    If your destructor have a `throw` statement, it should always be specified with `noexcept(false)`. Having the `throw` statement in a destructor without `noexcept(false)` is a big code-smell, it doesn't matter that it will (or rather *should*) never happen. – Some programmer dude Oct 22 '17 at 13:39
  • 5
    This seems like an XY problem. If you want the program to terminate then just call terminate directly. – wally Oct 22 '17 at 13:40
  • 1
    You can try setting `terminate` handler using `std::set_terminate`. And perform check inside of it. – user7860670 Oct 22 '17 at 13:42
  • @EmeraldWeapon - And array access should never be out of bounds... but stuff happen sometimes, which shouldn't. – StoryTeller - Unslander Monica Oct 22 '17 at 13:42
  • @StoryTeller Are you recommending to throw from destructors and be ok with that because bad stuff happens anyway? – Emerald Weapon Oct 22 '17 at 13:46
  • @EmeraldWeapon - Where did I say that? You are aware I hope that those principles, while good, are still not law? Some requirements cannot be programmed around. – StoryTeller - Unslander Monica Oct 22 '17 at 13:48
  • 2
    Throwing inside a `noexcept` function (destructor or not) will unconditionally call `terminate()`. That's a hint that it's something a well-written C++ application should NOT be doing. – rustyx Oct 22 '17 at 13:49
  • Although the purpose of the OP is to terminate the program.... – Emerald Weapon Oct 22 '17 at 13:49
  • @StoryTeller You didn't say that, just asking for clarification on what you actually meant. – Emerald Weapon Oct 22 '17 at 13:51
  • @EmeraldWeapon - I would think that what I meant is obvious. I'm certainly not recommending to access out of bounds for giggles either. But it happens. And that `error_that_should_never_ever_happen` could happen. If the destructor has no recourse but to throw (the invariants cannot be kept), what choice does it have? The OP's description is too vague, but situations with throwing destructors can arise in the wild. They are a sign of something far worse, for sure, but they still may be inescapable. That's why the "never" in your comment felt too strong to me. And why I commented. – StoryTeller - Unslander Monica Oct 22 '17 at 13:55
  • @Pinte - If your intent is to terminate, do so explicitly and gracefully. Don't hack it with a violated `noexcept` specification. And whatever you do, **document** it with big bold letters. – StoryTeller - Unslander Monica Oct 22 '17 at 13:57
  • @PInte You may find it useful to check out [this] (https://stackoverflow.com/questions/30250934/how-to-end-c-code) answer. Throwing from destructors can be really dangerous even if you are only trying to quit the program. Things may fail to terminate smoothly simply because other parts of your code do not expect a destructor to throw. – Emerald Weapon Oct 22 '17 at 14:06
  • @EmeraldWeapon my description is vague but is also more general. I don't want to make public the actual code because It could be more confusing and that isn't my purpose. But if you need the details I have 2 private functions grab and release that increment and decrement a private unsigned int variable x that is initialized with value 1 and release is called only AFTER the if and only in the destructor. The error_that_should_never_ever_happen is when x is 0. The only ways that could happen is if grab is called ULONG_MAX times before or someone messes up with the encapsulation of x. – Pinte Laurentiu Oct 22 '17 at 14:28
  • @StoryTeller my question is "How to test destructors that throw exceptions C++" so I don't use exceptions I use some other way to terminate my application some nice way but I still have the problem if and how do I test the applications behavior in case error_that_should_never_ever_happen does happen? – Pinte Laurentiu Oct 22 '17 at 14:45
  • 1
    If this behavior happens (and you trigger it in some way for testing) then call terminate directly. After that you should rely on a core dump or or other mechanism from the operating system to evaluate the call stack to confirm the terminate was called as you had hoped. Relying on the program to report on its own invalid state is fraught. – wally Oct 22 '17 at 16:58

0 Answers0