anatolyg
anatolyg

Reputation: 28300

How to catch exceptions conditionally?

My large application has this structure:

int main()
{
    try {
        ...
    } catch (std::exception& e) {
        std::cout << "Fatal error: " << e.what() << (some more data) << std::endl;
        return 1;
    }
}

Deep inside the call stack, various objects check their internal state, throwing std::runtime_exception if they detect something bad. The all-inclusive exception handler catches it, prints some moderately useful info and terminates the program.

However, when I am debugging under MS Visual Studio, I could benefit from not having any exception handler: Visual Studio has its own, very useful, handler, which stops my application at the place where the exception is thrown, so I can examine what went wrong.

How can I do the catching of my exceptions conditionally?

I tried the following:

    try {
        ...
    } catch (std::exception& e) {
        if (IsDebuggerPresent())
            throw;
        else
            std::cout << "Fatal error: " << e.what() << (some more data) << std::endl;
    }

This gave a weird result: Visual Studio caught the exception that was rethrown and showed me the stack trace at the point where the exception was thrown. However, all objects in my application were apparently destructed, and I couldn't see e.g. local or member variables.

I could make the exception handler conditional on a compilation flag:

#ifdef NDEBUG
    try {
#endif
        ...
#ifdef NDEBUG
    } catch (std::exception& e) {
        std::cout << "Fatal error: " << e.what() << (some more data) << std::endl;
    }
#endif

but this is inconvenient because I have to recompile everything if I want to debug it.

So, how can I make my exception handling conditional (depending on a command-line argument, for example)?

Upvotes: 8

Views: 1842

Answers (3)

Audrius Meškauskas
Audrius Meškauskas

Reputation: 21778

This is something far from great but you can define command line parameter in CMake (assuming you use it):

-DCATCH_ALL=true

Then, in CMakelists.txt, you can propagate this to C++ macro:

if (CATCH_ALL)
  message("Some exceptions with not be caught")
  add_compile_definitions(CATCH_ALL)
else ()
  message("Trying to catch more exceptions to provide better diagnostics")
endif ()

And finally, in the code:

#ifdef CATCH_ALL
try {
#endif
  a = big_problematic_algorithm(problematic_parameter);
#ifdef CATCH_ALL
 } catch (const std::exception &err) {
  LOGF(WARN, "Crashed for the parameter %s: %s", problematic_parameter.c_str(), err.what());
 }
#endif

It is rather clumsy but OK if not used too often, only somewhere at very high level (handling a complete web request, user action, file to process or the like). It allows to provide better diagnostic logs if the crash happens not while running in IDE, also to recover if you throw this exception yourself so know how.

It is possible to configure IDE to set the CMake parameter. When building on the integration server or from the command line, this is just not done activating additional handling of exceptions.

Upvotes: 1

anatolyg
anatolyg

Reputation: 28300

As suggested by CompuChip, Visual Studio can break execution when throwing an exception, not only when catching an uncaught one!

To enable this (in Visual Studio 2012):

  1. In the menu, go Debug -> Exceptions
  2. In the opened window, tick the "Thrown" box for all C++ exceptions (ticking just std::exception is not enough - I don't know why)
  3. Run your program

Upvotes: 1

utnapistim
utnapistim

Reputation: 27385

So, how can I make my exception handling conditional (depending on a command-line argument, for example)?

By writing the code for it :o]

Consider this original code:

int main()
{
    try {
        run_the_application(); // this part different than your example
    } catch (std::exception& e) {
        std::cout << "Fatal error: " << e.what() << (some more data) << std::endl;
        return 1;
    }
}

New code:

template<typename F>
int fast_run(F functor) { functor(); return EXIT_SUCCESS; }

template<typename F>
int safe_run(F functor)
{
    try {
        functor();
    } catch (std::exception& e) {
        std::cout << "Fatal error: " << e.what() << (some more data) << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

template<typename F>
int run(const std::vector<std::string>& args, F functor)
{
    using namespace std;
    if(end(args) != find(begin(args), end(args), "/d"))
        return fast_run(functor);
    else
        return safe_run(functor);
}

int main(int argc, char** argv)
{
    const std::vector<std::string> args{ argv, argv + argc };
    return run(args, run_the_application);
}

Upvotes: 2

Related Questions