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