简体   繁体   中英

How to make robust assert?

I want to realize this behavior:

  • When program runs in debug mode, assert_robust(expression, commands) works strictly like classical assert(expression)
  • When program runs in release mode, assert_robust(expression, commands) perform some commands if expression is false

This can be done this way:

#ifdef NDEBUG
    #define assert_robust(expression, command) if (!(expression)) command;
#else
    #define assert_robust(expression, command) assert(expression); 
#endif

And this can be used for example this way to make myfunction fault-tolerant:

char myfunction(const string& s, int i)
{
    assert_robust(i >= 0, return '\0');

    /* Normal code */
}

This work well, but how to make macro assert_robust that supports more than one (arbitrary) number of commands? (preferably by a standard C++ way)

And another question is:

Is it good thing to be strict in debug and benevolent in release?

EDIT: My idea why do such a thing is because it is practicaly much better if it is a bug in the program that the program maintains sometimes a little weird than when it crashes and user losing their data.

The more interesting question is the second:

Is it good thing to be strict in debug and benevolent in release?

My experiences is that it is a horrible idea to have different behavior in debug and release builds. You are signing up for issues in production that you will never be able to reproduce in a debug build because the behavior of the two is different.

Other than that, which you may claim won't be an issue if you assert in the first place in debug mode, asserts should be used to flag programming issues, situations from which you cannot recover safely. If you can recover safely in release mode, why assert in DEBUG? If you cannot, are you willing to fiddle with production data in a way you don't quite understand what it will do?

I don't think to use assertions this way is a good idea. Usually you use an assert, if you want the predicate to be always true, because its part of critical code. If its not true, than there is obviously a big problem and aborting is reasonable. But more and more people use assert like an ordinary error check for debugging. In this case its adequate to disable it completely in release mode. It think you should decide for one of this two approaches.

But if you want to run some kind of emergency commands before aborting, you could use the new lambda functions from C++11:

void ASSERT(int expression, std::function<void()> func) {
    if(!expression) {
        if (func) func();
        abort();
    }
}

You could use it like this:

ASSERT(a >= 0, []() { std::cerr << "ERROR" << std::endl;});

Or:

ASSERT(a >= 0, [this]() { this->terminate(); });

Or:

ASSERT(a >= 0, nullptr);

Without getting into the issue of if this is a good idea or not, you can use your macro to wrap multiple commands in a do-while(0); loop.

#ifdef NDEBUG
    #define assert_robust(expression, command) if (!(expression)) \
                                                 do{command;} while(0)
#else
    #define assert_robust(expression, command) assert(expression)
#endif

Note also that I did not include semicolons at the ends of the macros. If you include them in the macros, then something like

assert_robust(cond1, command1) /* no semicolon here, no problem */
assert_robust(cond2, command2) /* no semicolon here, no problem */

would be allowed, which would be really weird.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM