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.

 

  Asserts On The PocketPC
  Submitted by



I’ve frequented Flipcode for over 4 years now, but never submitted anything. Figured it was about time. I’ve been working on the PocketPC for the last 4 months and one of the things I kept running into was a lack of a decent assert. I finally broke down and decided to write my own collection of asserts and figured others on Flipcode might find them useful. First a little bit of background on the platform and its current implementation of assert. The PocketPC lacks a large portion of the C-stdlib. Things like abort, assert, fread, etc, don’t exist in the PocketPC libraries. To further complicate things, the PocketPC targets 3 significantly different processors, MIPS, SHx, and StrongARM so doing a simple Assert macro of __asm{ int 3 } doesn’t work. However, Microsoft has provided a macro for inserting break points that’s platform independent, DebugBreak. This macro is defined in winbase.h or kfuncs.h. Unfortunately DebugBreak does nothing unless a debugger is attached. On the PocketPC debugging is SLOW unless you have a model with a CompactFlash slot and own a CF Ethernet adapter. I wanted something that would warn me (or a tester) visually of asserts and stop the program if desired with out requiring the debugger to be attached. I figured logging to a file would prove useful as well. To use my implementation you must first Initialize the assert module. This is done through the INITASSERT macro. It’s not necessary, but if you want a log to be kept or your Assert message boxes to be modal then you’ll need to do this. There are two macros provided for the actual assert, ASSERT and ASSERTE. ASSERT converts the expression that was evaluated into a string and outputs that in the message box while ASSERTE takes a second parameter for an error message to be displayed instead of the expression. The logging looks to see if the log file exists already. If the file exists then it appends otherwise it’ll create the log file. Logging occurs regardless of the Assert being ignored or not. Logging doesn’t occur if the assert expression doesn’t evaluate to false however. If someone does decide they want to log passed asserts it would be fairly easy to create another macro or modify the included ones to do so. Lastly, there is zero overhead when compiled in release mode. There are no restrictions on how this code is used. Comments, questions, etc are welcome. Thanks,
-Lucas “Hosed” Goodwin

P.S. I KNOW there’s going to be something mentioned about using C++ exception handling, etc. Here’s a nice little tidbit. PocketPCs don’t support C++ Exception Handling. It’s Win32 Structured Exception Handling or bust. I’d love to see a TOTD or COTD covering Win32 SEH if anyone’s interested ;)

Currently browsing [StaticAssert.zip] (3,328 bytes) - [StaticAssert.h] - (3,745 bytes)

/******************************************************************************
  File Name: StaticAssert.h
  Description: Header for Debug Macros and Support Functions.
  Author: Lucas Goodwin (lucasg@gorge.net)
  Project: Pocket PC StaticLib
  Copyright (C) 2001 Goodwin Games -- All Rights Reserved

INITASSERT - Must be called before any asserts take place with in WinMain. Recommended that it be placed immediately following main window creation. If an invalid window handle is passed then assert message box will not be modal.

ASSERT - If expression evaluates to false... Breaks to debugger if attached, displays a message window indicating the expression that failed, in what file, and what line. If not ignored then program terminates with code -1. The assert is appended to log file indicated by INITASSERT regardless of ignore.

ASSERTE - Behaves same as ASSERT except expression is replaced by the passed error message.

EX: INITASSERT( hWnd, "/assert.log" ); ASSERT( 1 == 0 ); ASSERTE( 1 == 0, "Just testing ASSERTE" );

******************************************************************************/
#if !defined(__STATICLIB_STATICASSERT__) #define __STATICLIB_STATICASSERT__

#include "StaticIncludes.h"

//----------------------------------------------------------------------------- //Assert Macros //----------------------------------------------------------------------------- // Clear Macros #ifdef ASSERT #undef ASSERT #undef ASSERTE #undef INITASSERT #endif //ASSERT //Redefine Macros #if !defined(NDEBUG) //INITASSERT #define INITASSERT(hwnd, logfile) SL_Assert::init_assert(hwnd, logfile) //ASSERT - Output expression as string in Assert MsgBox #define ASSERT(exp) \ do { \ if( !(exp) ) \ { \ DebugBreak(); \ SL_Assert::assert(exp, TEXT(#exp), TEXT(__FILE__), \ TEXT(__TIMESTAMP__), __LINE__); \ } \ } while (0)

//ASSERTE - Output error message in Assert MsgBox #define ASSERTE(exp, errmsg) \ do { \ if( !(exp) ) \ { \ DebugBreak(); \ SL_Assert::assert(exp, TEXT(errmsg), TEXT(__FILE__), \ TEXT(__TIMESTAMP__), __LINE__); \ } \ } while (0) #else #define INITASSERT(hwnd, logfile) void(0) #define ASSERT(exp) void(0) #define ASSERTE(exp, errmsg) void(0) #endif // DEBUG

//----------------------------------------------------------------------------- // Assert Support Functions/Objects //----------------------------------------------------------------------------- #if !defined(NDEBUG)

namespace SL_Assert { //--------------------------------- //Namespace Globals //--------------------------------- static HWND g_hAssertMainWnd = NULL; //Handle to main window of app static TCHAR g_szFileName[512]; //String holding file name //--------------------------------- //Namespace Objects //--------------------------------- class AssertLogFile { HANDLE hFile; public: AssertLogFile( TCHAR *FileName ); ~AssertLogFile(); BOOL Append( char *szLogMessage ); };

//--------------------------------- //Namespace Functions //--------------------------------- //init_assert - Set Main Window and Log File Name for use in assert void init_assert( HWND hWnd, TCHAR *LogFileName );

//assert - Display error message box, log assert, and exit (if so desired) void assert( int exp, TCHAR *errmsg, TCHAR *filename, TCHAR *timestamp, int linenum );

//log_assert - Log assert message to log file, close File-Map object void log_assert( TCHAR *assertmsg ); }

#endif //NDEBUG

#endif //__STATICLIB_STATICASSERT__

Currently browsing [StaticAssert.zip] (3,328 bytes) - [StaticAssert.cpp] - (4,650 bytes)

/******************************************************************************
  File Name: StaticAssert.cpp
  Description: Implementation for Debug Macros and Support Functions.
  Author: Lucas Goodwin (lucasg@gorge.net)
  Project: Pocket PC StaticLib
  Copyright (C) 2001 Goodwin Games -- All Rights Reserved

See StaticAssert.h for details

******************************************************************************/
#include "StaticAssert.h"

//----------------------------------------------------------------------------- // Assert Support Functions //----------------------------------------------------------------------------- #if !defined(NDEBUG)

#define ASSERT_MSG_LEN 1024

//--------------------------------- //AssertLogFile Methods //--------------------------------- //AssertLogFile CTOR SL_Assert::AssertLogFile::AssertLogFile( TCHAR *FileName ) { //Open Log file hFile = CreateFile( FileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

//A file was opened? if( INVALID_HANDLE_VALUE == hFile ) { return; }

//If already exists, lets prepare to append by moving to end of file if( ERROR_ALREADY_EXISTS == GetLastError() ) { DWORD dwPtr = SetFilePointer( hFile, 0, NULL, FILE_END );

//Did we move properly? if( 0xFFFFFFFF == dwPtr ) { //Oops, something messed up, close the file and set hFile to //invalid handle CloseHandle( hFile );

//Closed the file, invalidate handle hFile = INVALID_HANDLE_VALUE; } } }

//AssertLogFile DTOR SL_Assert::AssertLogFile::~AssertLogFile() { //If handle is valid then, append new data and close the file if( INVALID_HANDLE_VALUE != hFile ) { //Append data to file by extending the EOF pointer SetEndOfFile( hFile );

//Close file CloseHandle( hFile ); } }

//AssertLogFile Append BOOL SL_Assert::AssertLogFile::Append( char *szLogMessage ) { BOOL Result = FALSE;

if( INVALID_HANDLE_VALUE != hFile ) { DWORD BytesWritten = 0; Result = WriteFile( hFile, (LPCVOID)szLogMessage, strlen(szLogMessage), &BytesWritten, NULL ); }

return Result; }





//--------------------------------- //Functions //--------------------------------- //init_assert void SL_Assert::init_assert( HWND hWnd, TCHAR *LogFileName ) { g_hAssertMainWnd = hWnd; wsprintf( g_szFileName, TEXT("%s"), LogFileName); }

//assert void SL_Assert::assert( int exp, TCHAR *errmsg, TCHAR *filename, TCHAR *timestamp, int linenum ) { if( !(exp) ) { int Result; TCHAR szErrMsg[ASSERT_MSG_LEN];

//Error Message for Log wsprintf( szErrMsg, TEXT("%s\nFile: %s\nLine: %i\nCompiled: %s\n\n"), errmsg, filename, linenum, timestamp );

//Log Assert log_assert( szErrMsg );

//Error Message for Message Box wsprintf( szErrMsg, TEXT("%s\n\nFile: %s\nLine: %i\n\nIgnore?"), errmsg, filename, linenum ); //Popup Message box... Result = MessageBox( g_hAssertMainWnd, szErrMsg, TEXT( "ASSERTION FAILURE" ), MB_YESNO | MB_ICONERROR | MB_SETFOREGROUND | MB_DEFBUTTON1 | MB_APPLMODAL );

//Respond based on User input to AssertBox switch( Result ) { //Assert ignored, continue case IDYES: break;

//Assert not ignored, Exit case IDNO: // Terminate Application exit(-1); break; } } }

//log_assert void SL_Assert::log_assert( TCHAR *assertmsg ) { AssertLogFile Log( g_szFileName ); char *ansi_log_break = "====================\n"; char ansi_assert_msg[ASSERT_MSG_LEN]; SYSTEMTIME CurSysTime;

//Append message seperator Log.Append( ansi_log_break );

//Time stamp this entry GetSystemTime( &CurSysTime ); sprintf( ansi_assert_msg, "%i:%i:%i - %i/%i/%i\n", CurSysTime.wHour, CurSysTime.wMinute, CurSysTime.wSecond, CurSysTime.wDay, CurSysTime.wMonth, CurSysTime.wYear ); Log.Append( ansi_assert_msg );

//Append message seperator Log.Append( ansi_log_break );

//PocketPC - All strings are unicode, lets switch to Ansi // Makes reading log file on PC easier int Result = WideCharToMultiByte( CP_ACP, NULL, (LPCWSTR)assertmsg, -1, (LPSTR)ansi_assert_msg, ASSERT_MSG_LEN, NULL, NULL );

//Did I convert from Unicode to Ansi? if( !Result ) { //Didn't convert properly, just output unicode string Log.Append( (char *)assertmsg ); } else { //Converted properly, append converted Ansi string to Log file Log.Append( ansi_assert_msg ); } }

#endif //NDEBUG

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.