HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcppCritical

How to end C++ code

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
codehowend

Problem

I would like my C++ code to stop running if a certain condition is met, but I'm not sure how to do that. So just at any point if an if statement is true terminate the code like this:

if (x==1)
{
    kill code;
}

Solution

There are several ways, but first you need to understand why object cleanup is important, and hence the reason std::exit is marginalized among C++ programmers.
RAII and Stack Unwinding

C++ makes use of a idiom called RAII, which in simple terms means objects should perform initialization in the constructor and cleanup in the destructor. For instance the std::ofstream class [may] open the file during the constructor, then the user performs output operations on it, and finally at the end of its life cycle, usually determined by its scope, the destructor is called that essentially closes the file and flushes any written content into the disk.

What happens if you don't get to the destructor to flush and close the file? Who knows! But possibly it won't write all the data it was supposed to write into the file.

For instance consider this code

#include 
#include 
#include 

void inner_mad()
{
    throw std::exception();
}

void mad()
{
    auto ptr = std::make_unique();
    inner_mad();
}

int main()
{
    std::ofstream os("file.txt");
    os << "Content!!!";

    int possibility = /* either 1, 2, 3 or 4 */;
    
    if(possibility == 1)
        return 0;
    else if(possibility == 2)
        throw std::exception();
    else if(possibility == 3)
        mad();
    else if(possibility == 4)
        exit(0);
}


What happens in each possibility is:

  • Possibility 1: Return essentially leaves the current function scope, so it knows about the end of the life cycle of os thus calling its destructor and doing proper cleanup by closing and flushing the file to disk.



  • Possibility 2: Throwing a exception also takes care of the life cycle of the objects in the current scope, thus doing proper cleanup...



  • Possibility 3: Here stack unwinding enters in action! Even though the exception is thrown at inner_mad, the unwinder will go though the stack of mad and main to perform proper cleanup, all the objects are going to be destructed properly, including ptr and os.



  • Possibility 4: Well, here? exit is a C function and it's not aware nor compatible with the C++ idioms. It does not perform cleanup on your objects, including os in the very same scope. So your file won't be closed properly and for this reason the content might never get written into it!



  • Other Possibilities: It'll just leave main scope, by performing a implicit return 0 and thus having the same effect as possibility 1, i.e. proper cleanup.



But don't be so certain about what I just told you (mainly possibilities 2 and 3); continue reading and we'll find out how to perform a proper exception based cleanup.
Possible Ways To End
Return from main!

You should do this whenever possible; always prefer to return from your program by returning a proper exit status from main.

The caller of your program, and possibly the operating system, might want to know whether what your program was supposed to do was done successfully or not. For this same reason you should return either zero or EXIT_SUCCESS to signal that the program successfully terminated and EXIT_FAILURE to signal the program terminated unsuccessfully, any other form of return value is implementation-defined (§18.5/8).

However you may be very deep in the call stack, and returning all of it may be painful...
[Do not] throw an exception

Throwing an exception will perform proper object cleanup using stack unwinding, by calling the destructor of every object in any previous scope.

But here's the catch! It's implementation-defined whether stack unwinding is performed when a thrown exception is not handled (by the catch(...) clause) or even if you have a noexcept function in the middle of the call stack. This is stated in §15.5.1 [except.terminate]:

  • In some situations exception handling must be abandoned for less subtle error handling techniques. [Note: These situations are:



[...]

— when the exception handling mechanism cannot find a handler for a thrown exception (15.3), or when the search for a handler (15.3) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4), or [...]

[...]

  • In such cases, std::terminate() is called (18.8.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called [...]



So we have to catch it!
Do throw an exception and catch it at main!

Since uncaught exceptions may not perform stack unwinding (and consequently won't perform proper cleanup), we should catch the exception in main and then return a exit status (EXIT_SUCCESS or EXIT_FAILURE).

So a possibly good setup would be:

``
int main()
{
/ ... /
try
{
// Insert code that will return by throwing a exception.
}
catch(const std::exception&) // Consider using a custom exception type for intentional
{ // throws. A good idea might be a
return_exception`.
return

Code Snippets

#include <fstream>
#include <exception>
#include <memory>

void inner_mad()
{
    throw std::exception();
}

void mad()
{
    auto ptr = std::make_unique<int>();
    inner_mad();
}

int main()
{
    std::ofstream os("file.txt");
    os << "Content!!!";

    int possibility = /* either 1, 2, 3 or 4 */;
    
    if(possibility == 1)
        return 0;
    else if(possibility == 2)
        throw std::exception();
    else if(possibility == 3)
        mad();
    else if(possibility == 4)
        exit(0);
}
int main()
{
    /* ... */
    try
    {
        // Insert code that will return by throwing a exception.
    }
    catch(const std::exception&)  // Consider using a custom exception type for intentional
    {                             // throws. A good idea might be a `return_exception`.
        return EXIT_FAILURE;
    }
    /* ... */
}

Context

Stack Overflow Q#30250934, score: 490

Revisions (0)

No revisions yet.