4
votes

I have ASSERT(x) macro and I want to call return if it asserts (in release configuration). To do this I need to know return type of function where I use this ASSERT. How to get it (I deal with C++03, LLVM GCC 4.2 compiler)?

My ASSERT macro:

#define ASSERT(x) \
    if(!(x)) {
        LOG ("ASSERT in %s: %d", __FILE__, __LINE__); \
        return /*return_type()*/; \
    }

PS: I tried return 0; - compiler shows error for void functions (and I didn't try it for complex returning types), if return; - error for non-void functions.

(Updated...)

I'll answer to werewindle, nyarlathotep and jdv-Jan de Vaan here. I use standard assert for debug configuration. But after beta-testing I still get crash reports from final customers, and in most cases I need to change my crashing functions:

ASSERT (_some_condition_);
if (!_some_condition_)      // add this return
    return _default_value_;

I understand, that my program may crash probably later (otherwise it will definitely crash in current function). Also I can't quit application because developing is for iPhone (apps may not quit programmatically there). So the easiest way would be to "auto return" when assertion failed.

8
When they say that apps can't just quit programmatically, they don't mean that you should just keep running no matter what bugs your program has. They mean to say that your app should be bug-free before submitting it! - Cody Gray
iPhone - isn't that Objective-C and not C++? - codeling
Have you thought about using exceptions? It seems like you could have a very high-level try-catch block which then logs the message that you could incorporate into your exception object (along with the location given by __FILE__ and __LINE__). You don't seem to be interested in the return value at all, are you? - Andre

8 Answers

4
votes

You can't determine the return type of the surrounding function in a Macro; macros are expanded by the preprocessor, which doesn't have this kind of information about the surroundings where these macros occur; it is basically just "searching and replacing" the macros. You would have to write separate macros for each return type.

But why not exit the program (i.e. calling the exit function)? Just returning from a function doesn't seem like a very robust error handling. Failed assertions should after all only occur when something is terribly wrong (meaning the program is in a state it was not designed to handle), so it is best to quit the program as soon as possible.

2
votes

There are no proper way to determine return type inside a function in C.

Also, if you somehow implement your variant of ASSERT it will lead to erroneous program behavior. Main idea of ASSERT: if it fails, then program is in undefined state and only proper way is to stop it now. I.e. by calling exit().

1
votes

i think you can do this with a template function, that you call default(x) from within the macro.

template<class T> default<T>(T x) { return T(); }

that will work for everyting with a default constructor. I think you need to write a special macro for void.

I hope i got the template syntax right, my c++ is getting rusty.

1
votes

You can't do that, the C/C++ preprocessor is pretty basic and it can't do any code analysis. At most what you can do is pass the return type to the macro.

But here's my opinion: you're using assertions the wrong way. They should only be used for sanity checks of the code (for errors than can only happen because of the programmer); if all assertions pass, you don't need to care about them, you don't need to log them.

And not only that, but (in general) you should employ the element of least surprise. Do you expect ASSERT to log something and then forcefully make the function return? I know I wouldn't. I either expect it to close the application completely (what the standard assert does) or let me decide what happens next (maybe I have some pointers to free).

0
votes

Macros do not return values, as they are no functions per se. They substitute the source code where they are used, so you'd return in the function where the macro is used.

There is no way to get the return value from a macro.

0
votes

You could just define another macro for your needs.

#define ASSERT(x) \
    if(!(x)) { \
        LOG ("ASSERT in %s: %d", __FILE__, __LINE__); \
        ASSERT_DEFAULT_RETURN(); \
    }

And then inside a function:

int foo(){
#ifdef ASSERT_DEFAULT_RETURN
#undef ASSERT_DEFAULT_RETURN
#endif
#define ASSERT_DEFAULT_RETURN() return 0
  // ...
  ASSERT(some_expression);
  // ...
  // cleanup
#undef ASSERT_DEFAULT_RETURN
}
0
votes

Just do this

#define ASSERT(x, ret, type)    \
     if(!(x)){
          LOG ("ASSERT in %s: %d", __FILE__, __LINE__); \
          return (type) ret;
     }
0
votes

I believe you are trying to solve the wrong problem. You don't want your program to crash in case of assertions, you best improve your testing.

Having a 'return' in these assertions give you a false sense of security. Instead it hides your problems and causes unexpected behavior in your program so the bugs you do get are much more complex. A colleague of mine actually wrote a good blog post about it.

If you really would want it, you could try writing return {}; so it default constructs the value, or have an assert macro where you also provide the failure case. However I really don't recommend it!