See what's going on with flipcode!




This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  An assert() Replacement
  Submitted by



assert() can be very useful for catching conditions that should never happen. However, the assert() function included inside could be a little better. After reading through some suggestions and code found in Game Programming Gems, I decided to write a replacement.

The new macro does a few useful things:

1. Breaks on the line in your code where the assert statement is located when 'Debug' is clicked. assert() breaks inside ASSERT.C which is highly annoying.
2. Adds the 'ignore always' functionality mentioned in GPG. Useful if an assert is firing repeatedly in a game loop.
3. Copies assert information into the clipboard so testers can easily mail you a copy of the error.
4. Provides a custom dialog, that hopefully looks better than assert()'s.
5. Provides a 2 parameter version to allow printing a message with the assert. sAssertM(file != NULL, "Unable to open file.");


Although this is written in Win32, it should be easy to convert it to linux/etc. Though it is possible, this version does not dump the call stack. I have another version that does, but it is nearly triple the current amount of code. For an example of how to do this (Win32 only), ask me or see the SUPERASSERT macro here: http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm

Screenshot:

Currently browsing [SimpleAssert.zip] (5,627 bytes) - [resource.h] - (960 bytes)

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by SimpleAssert.rc
//
#define IDIGNOREALWAYS                  6
#define IDEXIT                          7
#define IDD_CORE_ASSERT                 101
#define IDD_SIMPLE_ASSERT               102
#define IDC_FILENAME                    1000
#define IDC_EXPRESSION                  1001
#define IDC_MESSAGE                     1002
#define IDC_LINENUMBER                  1003
#define IDDEBUG                         1004
#define IDC_CALLSTACK                   1005
#define IDC_EDIT1                       1006
#define IDC_BUTTON1                     1007

// Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1008 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif

Currently browsing [SimpleAssert.zip] (5,627 bytes) - [SimpleAssert.cpp] - (3,966 bytes)

//****************************************************************************
//**
//**    cAssert.cpp
//**    Source - Replacement assert functions.
//**
//****************************************************************************
#include <windows.h>
#include <stdio.h>
#include "SimpleAssert.h"

#include "resource.h" //============================================================= #ifdef _DEBUG

extern HINSTANCE g_hInst;

static const char* _s_expStr = NULL; static const char* _s_msg = NULL; static const char* _s_file = NULL; static char _s_lineNum[12];

enum { AD_DEBUG = 0x01, AD_IGNORE = 0x02, AD_IGNOREALWAYS = 0x03, AD_EXIT = 0x04 };

//============================================================= // AssertDlgProc INT_PTR CALLBACK AssertDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALOG: { ::SetWindowText(::GetDlgItem(hDlg, IDC_FILENAME), _s_file);

::SetWindowText(::GetDlgItem(hDlg, IDC_LINENUMBER), _s_lineNum);

::SetWindowText(::GetDlgItem(hDlg, IDC_EXPRESSION), _s_expStr);

::SetWindowText(::GetDlgItem(hDlg, IDC_MESSAGE), _s_msg);

char modulePath[MAX_PATH]; GetModuleFileName(NULL, modulePath, MAX_PATH);

const char* moduleName = strrchr(modulePath, '\\'); moduleName = moduleName ? moduleName+1 : modulePath;

char title[MAX_PATH + 20]; sprintf(title, "Assert Failed: %s", moduleName); SetWindowText(hDlg, title);

// Paste it to clipboard: if (OpenClipboard(NULL)) { HGLOBAL hMem; char buf[1024]; char *pMem;

sprintf(buf, "Application: %s\r\nFilename: %s (%s)\r\nExpression: %s\r\nMessage: %s\r\n", moduleName, _s_file, _s_lineNum, _s_expStr, _s_msg ? _s_msg : "");

hMem = GlobalAlloc(GHND|GMEM_DDESHARE, strlen(buf)+1); if (hMem) { pMem = (char*)GlobalLock(hMem); strcpy(pMem, buf); GlobalUnlock(hMem);

EmptyClipboard(); SetClipboardData(CF_TEXT, hMem); }

CloseClipboard(); GlobalFree(hMem); }

break; } case WM_COMMAND: { switch(LOWORD(wParam)) { case IDDEBUG: EndDialog(hDlg, AD_DEBUG); return TRUE;

case IDIGNORE: EndDialog(hDlg, AD_IGNORE); return TRUE;

case IDIGNOREALWAYS: EndDialog(hDlg, AD_IGNOREALWAYS); return TRUE;

case IDEXIT: EndDialog(hDlg, AD_EXIT); return TRUE;

} } break; }

return FALSE; }

//============================================================= // _sAssertDlg_ bool _sAssertDlg_(const char* expStr, const char* msg, const char* file, int line, bool* ignoreAlways) { DWORD lastErr = GetLastError();

// EnterCriticalSection(...) _s_expStr = expStr; _s_msg = msg;

// log it char logMsg[1024]; sprintf(logMsg, "%s(%i) : [Assert] (%s) %s\n", file, line, expStr, msg ? msg : ""); OutputDebugString(logMsg);

// calc last "\" pos _s_file = strrchr(file, '\\'); _s_file = _s_file ? _s_file+1 : file;

// convert line to a string itoa(line, _s_lineNum, 10);

// show dialog INT_PTR res; res = DialogBox(g_hInst, MAKEINTRESOURCE(IDD_SIMPLE_ASSERT), NULL, (DLGPROC) AssertDlgProc);

switch (res) { case AD_IGNOREALWAYS: *ignoreAlways = true;

case AD_IGNORE: return false;

case AD_DEBUG: return true;

case AD_EXIT: exit(0); break; }

// LeaveCriticalSection(...) SetLastError(lastErr); return true; }

#endif // _DEBUG

Currently browsing [SimpleAssert.zip] (5,627 bytes) - [SimpleAssert.h] - (1,028 bytes)

#ifndef __SimpleAssert_H__
#define __SimpleAssert_H__

//============================================================================ // An extended assert macro. This code is based on the ideas and code // from Game Programming Gems. //============================================================= #ifdef _DEBUG

bool _sAssertDlg_(const char*, const char*, const char*, int, bool*);

// sAssert #define sAssert(exp) { \ static bool _ignoreAlways_ = false; \ if (!_ignoreAlways_ && !(exp)) { \ if (_sAssertDlg_(#exp, NULL, __FILE__, __LINE__, &_ignoreAlways_)) \ { _asm { int 3 } } \ } \ }

// sAssertM #define sAssertM(exp, msg) { \ static bool _ignoreAlways_ = false; \ if (!_ignoreAlways_ && !(exp)) { \ if (_sAssertDlg_(#exp, msg, __FILE__, __LINE__, &_ignoreAlways_)) \ { _asm { int 3 } } \ } \ }

#else

#define sAssert(exp) #define sAssertM(exp, msg) #endif

#endif // __SimpleAssert_H__

Currently browsing [SimpleAssert.zip] (5,627 bytes) - [Test.cpp] - (1,151 bytes)

#include <windows.h>
#include <stdio.h>

#include "SimpleAssert.h"

//============================================================= HINSTANCE g_hInst = NULL;

//============================================================= int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_hInst = hInstance;

// Test 1 sAssert(1 == 2 == 3 == 4);

// Test 2 - sAssertM FILE * file; file = fopen("BLAHBLAH.bla", "rb");

sAssertM(file != NULL, "Unable to open file.");

// Test 3 - Always Ignore for (int i = 0; i < 100; i++) { bool alwaysFalse = false; sAssertM(alwaysFalse, "Try using Ignore Always"); // 'Ignore Always' will ignore a specific sAssert statement // during this execution. }

// Test 4 - Try 'Debug' sAssertM(1 + 1 == 3, "Try clicking Debug"); // <- should break here

MessageBox(NULL, "Test Complete", "Done", MB_OK);

// Note: If running in the debugger, // check the output for [Assert] lines. return 0; }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.