|
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.
|