Skip to main content
gamedev.net
🔒 Locked

assert...?

Started by gumpy Dec 30, 2006 at 3:37 PM 11 replies 2.7k views
Original Post
gumpy
gumpy
first i should mention that i originally started learning C a long time ago and then moved on to C++ before i really got into it. i've never really done anything in pure C and i thought i'd play around a bit just for the hell of it. it didn't take long before i decided i wanted something similar to assert() that doesn't get left out of a release build. i'm not familiar enough with the standard and have no idea if what i want already exists or not so i wrote the following macro:

#include <stdio.h>
#include <stdlib.h>

#define RTASSERT(expr,msg){if(!(expr)){fprintf(stderr,"runtime assertion error:\n\tfile: %s\n\tline: %d\n\tfunction: %s\n\texpression: %s\n\tmessage: %s\n",__FILE__,__LINE__,__FUNCTION__,(#expr),(msg));exit(EXIT_FAILURE);}}

int main( int argc, char *argv[] ) {
        RTASSERT( 2 + 2 == 5, "lol 2 + 2 != 5" );
        return EXIT_SUCCESS;
}


output:

runtime assertion error:
        file: foo.c
        line: 7
        function: main
        expression: 2 + 2 == 5
        message: lol 2 + 2 != 5
is there something in the standard i should be using instead or is there a better way to do this?
This space for rent.
Anon Mike
Anon Mike
You could call it VERIFY but that's not really right either.

The closest thing to a standard would be to just put real code in there instead of wrapping stuff up in a macro. If you're using C++ you can minimize the typing by using exceptions although that brings it's own can of worms if you're not already coding in an exception-safe way.
-Mike
Driv3MeFar
Driv3MeFar
I've seen this done before, but I can't say I particularly like it:

#define rassert(exp) ( if ( !exp ) _asm{int 3} )
gumpy
gumpy
i definitely prefer RAII and exceptions in c++ and java, but learning what to do in this case can only make me a better programmer.

i think the name i used is somewhat silly. i haven't thought of a better one yet.
This space for rent.
LessBread
LessBread
Does the compiler your using supply an "assert.h" file? If so, check it out.

In the meantime, here's a simplified assert.

// ---------------------------------------------------------------------------// The DebugBreak exported from K32 puts the debugger into K32 code// This macro puts the debugger into the code being debugged#ifdef _X86_#define DebugBreak() _asm("int3;")#endif// ---------------------------------------------------------------------------// ASSERT evaluates the parameter and if it is zero, breaks execution// In release mode, ASSERT should expand to nothing.// Should be used with expressions that do not include any function call// REMEMBER - an assert is intended to catch logic errors at compile time//#ifdef _DEBUG#define ASSERT(x) do { if( !(x) ) { DebugBreak(); } } while(0)#else#define ASSERT(x)#endif// ---------------------------------------------------------------------------// VERIFY valuates the parameter and if it is zero, breaks execution// In release mode, VERIFY should expand to its parameter// Use VERIFY for expressions that contain function calls#ifdef _DEBUG#define VERIFY(x) ASSERT(x)#else#define VERIFY(x) (x)#endif
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
gumpy
gumpy
Quote:
Original post by LessBread
Does the compiler your using supply an "assert.h" file? If so, check it out.

In the meantime, here's a simplified assert.

*** Source Snippet Removed ***


yep. i'm using gcc and assert is obviously extremely similar to what i'm doing.

to demonstrate the purpose, here's some (modified) code i posted in another thread earlier:
/* * DISCLAIMER: * i will not be held accountable for any damage or data loss * as a result of the following program. use this code at your * own risk. *  * yeah, i'm attempting to learn pure "C" again. *  */#include <stdio.h>#include <stdlib.h>#include "SDL/SDL.h"#include "SDL/SDL_image.h"typedef int BOOL;const BOOL FALSE = 0;const BOOL TRUE  = 1;#define ASSERT(expr,msg){if(!(expr)){fprintf(stderr,"ASSERT FAILED:\n\tFILE: %s\n\tLINE: %d\n\tFUNCTION: %s\n\tEXPRESSION: %s\n\tMESSAGE: %s\n",__FILE__,__LINE__,__FUNCTION__,(#expr),(msg));exit(EXIT_FAILURE);}}int main(int argc, char *argv[] ) {	SDL_Surface *screen = NULL;	SDL_Surface *bmp = NULL;	SDL_Surface *sprite = NULL;	SDL_Surface *temp = NULL;	BOOL done = FALSE;	ASSERT( 0 == SDL_Init(SDL_INIT_VIDEO), SDL_GetError() );	ASSERT( 0 == atexit(SDL_Quit), "atexit() failed" );	SDL_WM_SetCaption("OMFG I LIKE PI!", NULL);	screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE | SDL_DOUBLEBUF);	ASSERT( NULL != screen, SDL_GetError() );	bmp = SDL_LoadBMP("data/test.bmp");	ASSERT( NULL != bmp, SDL_GetError() );	temp = bmp;	bmp = SDL_DisplayFormat(bmp);	SDL_FreeSurface(temp);	ASSERT( NULL != bmp, SDL_GetError() );	sprite = IMG_Load("data/helmut.png");	ASSERT( NULL != sprite, SDL_GetError() );	temp = sprite;	sprite = SDL_DisplayFormatAlpha(sprite);	SDL_FreeSurface(temp);	ASSERT( NULL != sprite, SDL_GetError() );	while(!done) {		SDL_Event e;		while(SDL_PollEvent(&e)) {			switch(e.type) {				case SDL_KEYDOWN:					if(e.key.keysym.sym == SDLK_ESCAPE) {						done = TRUE;					}					break;				case SDL_QUIT:					done = TRUE;					break;				default:					break;			}		}		SDL_BlitSurface(bmp, NULL, screen, NULL);		SDL_BlitSurface(sprite, NULL, screen, NULL);		SDL_Flip(screen);	}	SDL_FreeSurface(bmp);	SDL_FreeSurface(screen);	return EXIT_SUCCESS;}

and output if "data/helmut.png" doesn't load (not found in this case)
ASSERT FAILED:	FILE: ../src/main.c	LINE: 41	FUNCTION: main	EXPRESSION: NULL != sprite	MESSAGE: Couldn't open data/helmut.png


i normally cringe at the thought of using macros but i don't see any way around it without losing convenience and clarity. i think i've gotten too used to java. [lol]
This space for rent.
erissian
erissian
Well, if you're going to go so far as to have runtime assertion, that looks fine. I do however have a general suggestion:

In this case, it's the end user who's getting the error, not you. You'll rely on a cooperative user to help you find the problem. Output a somewhat descriptive error to the user, and then append all of your debugging info to an error log.

In the user's error, provide an email address where they can mail the error log. In the error log itself, make sure to include the time and date the error happened, and the version/build number of the program they're using.

If you do this, then the user won't be overwhelmed with information, and you can see if there's a history of the problem on that machine, but you'll know whether you're seeing new errors or ones they've already reported. If you have the version number, it's possible they're running an older version and encountering a bug you've already fixed.
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
gumpy
gumpy
Quote:
Original post by erissian
Well, if you're going to go so far as to have runtime assertion, that looks fine. I do however have a general suggestion:

In this case, it's the end user who's getting the error, not you. You'll rely on a cooperative user to help you find the problem. Output a somewhat descriptive error to the user, and then append all of your debugging info to an error log.

In the user's error, provide an email address where they can mail the error log. In the error log itself, make sure to include the time and date the error happened, and the version/build number of the program they're using.

If you do this, then the user won't be overwhelmed with information, and you can see if there's a history of the problem on that machine, but you'll know whether you're seeing new errors or ones they've already reported. If you have the version number, it's possible they're running an older version and encountering a bug you've already fixed.


i couldn't agree more. i plan on expanding this quite a bit but for now i'm making sure i'm not going about this the wrong way. i feel far from home using pure c.
This space for rent.
LessBread
LessBread
Quote:
Original post by gumpy macdrunken
yep. i'm using gcc and assert is obviously extremely similar to what i'm doing.


Such macros are usually wrapped with a "do { ... } while(0)" so that the closing semi-colon has something to hang on to... [smile] Actually, it's to ensure that the assert is treated as a single statement. 3.9.3 Swallowing the Semicolon

And crafting your own assert function can make the code easier to deal with. For example:

void MyAssertFunk(char *file, char*line, char *funk, char *expr, char *msg){ fprintf(stderr,"ASSERT FAILED:\n\tFILE: %s\n\tLINE: %d\n\tFUNCTION: %s\n\tEXPRESSION: %s\n\tMESSAGE: %s\n",	file, line, funk, expr, msg); exit(EXIT_FAILURE);}#define ASSERT(expr,msg) do {  if ( !(expr) ) {  MyAssertFunk(__FILE__,__LINE__,__FUNCTION__,(#expr),(msg)); }  } while(0)


The backslash-newlines allow for splitting the macro across multiple lines.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Way Walker
Way Walker
Y'all's code is wreaking havoc in my browser.

void MyAssertFunk(char *file, char*line, char *funk, char *expr, char *msg){    fprintf(        stderr,        "ASSERT FAILED:\n"        "\tFILE: %s\n"        "\tLINE: %d\n"        "\tFUNCTION: %s\n"        "\tEXPRESSION: %s\n"        "\tMESSAGE: %s\n",	file, line, funk, expr, msg);    exit(EXIT_FAILURE);}#define ASSERT(expr,msg)do { \    if ( !(expr) ) { \        MyAssertFunk(__FILE__,__LINE__,__FUNCTION__,(#expr),(msg)); \    } \} while(0)


But, so far, why not use assert.h? Add some extra logging or just use assert(). [smile]

EDIT: To clarify, do you want two asserts, one that's debug only and one that's also in release builds? Some people recommend never taking asserts out of code if they don't do anything "expensive".

EDIT: I WIN! Backslashes are a pain...

Quote:

i normally cringe at the thought of using macros but i don't see any way around it without losing convenience and clarity


Then why cringe? Jumping through hoops to avoid macros makes me cringe. [smile]

Keep in mind that they're much less evil in C. For example, you no longer have the problem of stepping on namespaces.
LessBread
LessBread
Quote:
Original post by Way Walker
Y'all's code is wreaking havoc in my browser.


I thought the source box would resolve that... ???

The MyAssertFunk is just a throw away to demonstrate the principle.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
gdunbar
gdunbar
Your code looks fine, but you should try _really_ hard to ensure that the end user doesn't ever experience your assert, and instead sees the results of your proper error handling. Telling the user (for instance) that there is a problem with his video driver and potential solutions is much more helpful (to him) than telling him the file and line number where the problem occurred. :-) Some might argue that you shouldn't do "free build" asserts at all to encourage proper behavior, but I can see both sides of the argument.

Geoff
Way Walker
Way Walker
Quote:
Original post by LessBread
Quote:
Original post by Way Walker
Y'all's code is wreaking havoc in my browser.


I thought the source box would resolve that... ???


The source boxes aren't giving horizontal scroll bars so I'm having to scroll the entire window left/right to read posts.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.