|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic |
Last Thread Next Thread ![]() |
| ASSERT(), VERIFY() and TRACE() for non-MFC Applications |
|
![]() wolf XNA/DirectX MVP Member since: 1/8/2000 From: Carlsbad, CA, United States |
||||
|
|
||||
| Great and useful tip ! Could you provide us with a few example code snippets ? That would be great. - Wolf |
||||
|
||||
![]() phueppl1 Member since: 7/11/2000 From: Alkoven, Austria |
||||
|
|
||||
| hmmm I hate that when the < and > don't show up because of HTML.. solution ? besides that, thx! will try them when I come home.. cya, Phil |
||||
|
||||
![]() Gabriel Fleseriu Member since: 3/2/2002 From: Switzerland |
||||
|
|
||||
quote: The main idea is to have a simple and handy set of debugging macros that can be used w/o MFC. Create a new empty console project. Do not activate MFC support, no StdAfx.h, nothing. Create a new (empty) file and name it main.cpp. Add it to the project. Also add the file debug.cpp to the projrcts source files. Ensure that debug.h is in the header path (or copy it to the project dir). Add following code to main.cpp:
#include "debug.h"
int main()
{
int i=0, j=1, k=2;
TRACE("Hello debugger world, here some vars %d, %d, %d\n", i, j, k);
ASSERT(0);
return 0;
}
You will see Hello debugger world, here some vars 0, 1, 2appear in the debugger output window, and the program will stop (as if you had put a brekpoint) on the line ASSERT(0); You can add debug.cpp and debug.h to any kind of non-MFC project (Win32 Console, Win32 GUI app). TRACE() works like the old good printf(), with the difference that it prints to the debuggers output window. CU, Gabriel. Forever trusting who we are And nothing else matters - Metallica |
||||
|
||||
![]() Gabriel Fleseriu Member since: 3/2/2002 From: Switzerland |
||||
|
|
||||
quote: Solution? A mail to Dave. In the meanwhile:
//debug.h
#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef _DEBUG
void _trace(char *fmt, ...);
#define ASSERT(x) {if(!(x)) _asm{int 0x03}}
#define VERIFY(x) {if(!(x)) _asm{int 0x03}}
#else
#define ASSERT(x)
#define VERIFY(x) x
#endif
#ifdef _DEBUG
#define TRACE _trace
#else
inline void _trace(char* fmt, ...) { }
#define TRACE 1 ? (void)0 : _trace
#endif
#endif // __DEBUG_H__
//debug.cpp:
#ifdef _DEBUG
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
void _trace(char *fmt, ...)
{
char out[1024];
va_list body;
va_start(body, fmt);
vsprintf(out, fmt, body);
va_end(body);
OutputDebugString(out);
}
#endif
Forever trusting who we are And nothing else matters - Metallica [edited by - Gabriel Fleseriu on July 24, 2002 10:22:04 AM] |
||||
|
||||
![]() Technocrat Member since: 3/27/2001 |
||||
|
|
||||
| What if your output is more than 1024 chars? Wouldn't something like
inline void TRACE(char *fmt, ...)
{
size_t iSize = strlen(fmt);
char* out = new char[iSize+1];
va_list body;
va_start(body, fmt);
vsprintf(out, fmt, body);
va_end(body);
OutputDebugString(out);
delete[] out;
}
be better? |
||||
|
||||
![]() tobymurray Member since: 12/4/2001 From: Adelaide, Australia |
||||
|
|
||||
| Good useful sweet snippet. One thing I would add is that while perhaps the MFC ASSERT() just brekas into the debugger when an assert fails, it may be more useful to pop up a messagebox asking the user if they wish to break or not. I don't know if the MFC ASSERT() does this or not ( I suspect not from the article ) however it may be useful and easily implemented just using MessageBox() and passing the assertion as text using the # precompiler operator. just a thought Toby Gobsmacked - by Toby Murray |
||||
|
||||
![]() shurcool Member since: 5/6/2001 From: Toronto, Canada |
||||
|
|
||||
quote: not really... the idea might be good, but the execution isn't. what if i had something like this: TRACE("name is %s", player[0]->name); it would allocate 10 + 1 chars. those 11 chars are enough to fit "name is %s", but is it enough to fit "name is shurcool"? i hope you see what i'm saying. --- shurcool wwdev [edited by - shurcool on July 24, 2002 11:55:31 AM] |
||||
|
||||
![]() Khawk Staff Member since: The dawn of time
From: Port Orange, FL, United States |
||||
|
|
||||
quote: Actually, I'm the one that put this up.. I have no idea where Dave is right now, and we needed a new article. It's my first time editing one in over a year, so cut me some slack. Kevin "Khawk" Hawkins CEO and News Director, GameDev.net Author, OpenGL Game Programming Developer Diary |
||||
|
||||
![]() Technocrat Member since: 3/27/2001 |
||||
|
|
||||
quote: Hmm, good point. I guess 1024 is a safe number, but for something that is supposed to help when you have problems, it would seem to me that having a fixed size char VAR seems like an un-needed problem. Other than that good code. |
||||
|
||||
![]() Gabriel Fleseriu Member since: 3/2/2002 From: Switzerland |
||||
|
|
||||
quote: Yes, you could add as much functionality to ASSERT() as you would feel necessary. See the MFC implementation. I avoided MessageBox() and other fancy features because they don't really add value to the snippet, IMHO. Besides of thet: once the execution is interrupted - where is the difference between pressing F5 in VC++ or selecting "Yes" in the message box? |
||||
|
||||
![]() sark Member since: 4/16/2002 |
||||
|
|
||||
Inspired by this article i've written a more C++ approach to TRACE() some of you might find useful:
/////////////////////////////////////
// IN TRACE.H
#ifndef TRACE_H
#define TRACE_H
#ifdef _DEBUG
#include <iostream>
class Tracebuf : public std::streambuf
{
public:
int sync();
int overflow(int ch);
private:
void WriteToDebug(const char* pc, std::streamsize s);
};
extern std::ostream tout;
#define TRACE(x) tout << x
#else // _DEBUG
#define TRACE(x)
#endif // _DEBUG
#endif // TRACE_H
/////////////////////////////////////
// IN TRACE.CPP
#include "Trace.h"
#ifdef _DEBUG
static Tracebuf tbuf;
std::ostream tout(&tbuf);
int Tracebuf::sync()
{
std::streamsize n = pptr() - pbase();
if (n)
WriteToDebug(pbase(), n);
return n;
}
int Tracebuf::overflow(int ch)
{
std::streamsize n = pptr() - pbase();
if (n && sync())
return EOF;
if (ch != EOF)
{
char c(ch);
WriteToDebug(&c, 1);
}
pbump(-n);
return 0;
}
// pass as a char* as i believe the stream is not null terminated
void Tracebuf::WriteToDebug(const char* pc, std::streamsize s)
{
std::string str(pc, s);
::OutputDebugString(str.c_str());
}
#endif // _DEBUG
You can use any stream manipulators on it, like endl, and it will automatically format numbers for you - just like cout:
TRACE("Look at me, im debugging! : " << 111222333 << endl);
TRACE("And again, with that in hex: " << hex << 111222333 << endl);
Please bear in mind i've only just written it if you are gonna use it, so its minimally tested. [edit] - remember to use << endl or << flush at the end of any TRACE() calls, so the buffer'll flush immediately. If you're afraid you'll forget, just modify #define TRACE(x) tout << x to #define TRACE(x) tout << x << flush [edited by - sark on July 27, 2002 2:03:52 AM] |
||||
|
||||
![]() Shannon Barber Moderator Member since: 6/23/2000 From: Westland, MI, United States |
||||
|
|
||||
You can use the preprocessor directive NDEBUG to detect a release build portably (and the lack thereof signifies a debug build)#ifndef NDEBUG //Debug code #else //Release code #endif If you are concerned about string buffer overruns (I am) you can use std::stringstream instead of printf (I do). Or at the least use the n flavor of sprintf (nsprintf? snprintf?) so it doesn't corrupt the stack. |
||||
|
||||
![]() Mihail121 Banned Member since: 5/22/2002 From: Sofia, Bulgaria |
||||
|
|
||||
| Hey man.That was so cool to know but i have a question of course. This is part of my class function: bool COGLWindow::Create(char *strWindowName,int width,int height,int bpp) { if(!strWindowName || (*strWindowName) == '\0') { g_log.Add("Invalid window name"); return false; } return true; } So my question is would it be better if I use: ASSERT(strWindowName == NULL); ASSERT((*strWindowName) == '\0'); 10x; The pain is coming...this sommer!!! |
||||
|
||||
![]() Gabriel Fleseriu Member since: 3/2/2002 From: Switzerland |
||||
|
|
||||
quote: That would check whether the POINTER is 0 quote: This assumes a valid pointer and checks for empty string. First assert on a valid (i.e. non-null) pointer. Then dereference it and see if the string is not empty. Of course, the fact that a pointer is not 0 is no guarantee that the pointer is valid. It is good habit to initialize pointers to 0 and to set them 0 again after deletion. char *p = 0; //... p = new char[100]; //... use p delete[] p; p = 0; //... Forever trusting who we are And nothing else matters - Metallica [edited by - Gabriel Fleseriu on August 1, 2002 4:59:11 AM] |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
quote: Hmm, good point. I guess 1024 is a safe number, but for something that is supposed to help when you have problems, it would seem to me that having a fixed size char VAR seems like an un-needed problem. Other than that good code. The way to get around this in windows is with _vnsprintf();
inline void TRACE(const char *str, ...)
{
va_list marker;
int count = -1;
string message;
va_start(marker, str);
while(count == -1) {
char buf[1024];
// _vsnprintf returns -1 when it doesn't finish writing, or number of chars in buf
count = _vsnprintf(buf, 1023, str, marker);
OutputDebugString(buf);
}
return message;
}
|
||||
|
||||
![]() Technocrat Member since: 3/27/2001 |
||||
|
|
||||
| I think you did not mean to put string message; and return message; [edited by - Technocrat on August 5, 2002 12:57:00 PM] |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| For moderator or GDStaff: I submitted an article for posting on GDNET. I understand the staff is wrapped up in the judging of the 4E3 contest, but I submitted before then, and then during. I guess I will have to submit every week until I get a response, is that a prudent course of action? The article has been editted by 2 different people and there isn't any extensive coverage of the subject matter in the Articles and Resources section. The article is relavent to GDNET and can answer questions that have been repeatedly asked in the forums. Does it take this long to get a response? MKH: It can take some time to get a response for the acceptance of an article, as they like to read them over prior to giving it a thumbs up or down. If Dave decides to run it, it can take several months before it's actually posted. They like to keep a stockpile of articles available as a buffer, so they don't run out during periods of low or no submissions. [edited by - Magmai Kai Holmlor on August 21, 2002 11:46:41 PM] |
||||
|
||||
![]() dalleboy Member since: 4/14/2002 From: Umea, Sweden |
||||
|
|
||||
I took a look at MFC CString::Format and this should work for any length of string messages.
// Debug.h
#include <windows.h>
#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef _DEBUG
void _trace(LPCTSTR *fmt, ...);
#define ASSERT(x) {if(!(x)) _asm{int 0x03}}
#define VERIFY(x) {if(!(x)) _asm{int 0x03}}
#else
#define ASSERT(x)
#define VERIFY(x) x
#endif
#ifdef _DEBUG
#define TRACE _trace
#else
inline void _trace(LPCTSTR* fmt, ...) { }
#define TRACE 1 ? (void) 0 : _trace
#endif
#endif // __DEBUG_H__
// Debug.cpp
#include <Debug.h>
#include <tchar.h>
#include <malloc.h>
#define TCHAR_ARG TCHAR
#define WCHAR_ARG WCHAR
#define CHAR_ARG char
struct _AFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
#ifdef _X86_
#define DOUBLE_ARG _AFX_DOUBLE
#else
#define DOUBLE_ARG double
#endif
#define FORCE_ANSI 0x10000
#define FORCE_UNICODE 0x20000
#define FORCE_INT64 0x40000
// formatting (using wsprintf style formatting)
void _trace(LPCTSTR lpszFormat, ...)
{
va_list argList;
va_start(argList, lpszFormat);
va_list argListSave = argList;
// make a guess at the maximum length of the resulting string
int nMaxLen = 0;
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
{
// handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
{
nMaxLen += _tclen(lpsz);
continue;
}
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0;
for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
{
// check for valid flags
if (*lpsz == '#')
nMaxLen += 2; // for '0x'
else if (*lpsz == '*')
nWidth = va_arg(argList, int);
else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
*lpsz == ' ')
;
else // hit non-flag character
break;
}
// get width and skip it
if (nWidth == 0)
{
// width indicated by
nWidth = _ttoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
;
}
ASSERT(nWidth >= 0);
int nPrecision = 0;
if (*lpsz == '.')
{
// skip past '.' separator (width.precision)
lpsz = _tcsinc(lpsz);
// get precision and skip it
if (*lpsz == '*')
{
nPrecision = va_arg(argList, int);
lpsz = _tcsinc(lpsz);
}
else
{
nPrecision = _ttoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
;
}
ASSERT(nPrecision >= 0);
}
// should be on type modifier or specifier
int nModifier = 0;
if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
{
lpsz += 3;
nModifier = FORCE_INT64;
#if !defined(_X86_) && !defined(_ALPHA_)
// __int64 is only available on X86 and ALPHA platforms
ASSERT(FALSE);
#endif
}
else
{
switch (*lpsz)
{
// modifiers that affect size
case 'h':
nModifier = FORCE_ANSI;
lpsz = _tcsinc(lpsz);
break;
case 'l':
nModifier = FORCE_UNICODE;
lpsz = _tcsinc(lpsz);
break;
// modifiers that do not affect size
case 'F':
case 'N':
case 'L':
lpsz = _tcsinc(lpsz);
break;
}
}
// now should be on specifier
switch (*lpsz | nModifier)
{
// single characters
case 'c':
case 'C':
nItemLen = 2;
va_arg(argList, TCHAR_ARG);
break;
case 'c'|FORCE_ANSI:
case 'C'|FORCE_ANSI:
nItemLen = 2;
va_arg(argList, CHAR_ARG);
break;
case 'c'|FORCE_UNICODE:
case 'C'|FORCE_UNICODE:
nItemLen = 2;
va_arg(argList, WCHAR_ARG);
break;
// strings
case 's':
{
LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = lstrlen(pstrNextArg);
nItemLen = max(1, nItemLen);
}
}
break;
case 'S':
{
#ifndef _UNICODE
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = wcslen(pstrNextArg);
nItemLen = max(1, nItemLen);
}
#else
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = lstrlenA(pstrNextArg);
nItemLen = max(1, nItemLen);
}
#endif
}
break;
case 's'|FORCE_ANSI:
case 'S'|FORCE_ANSI:
{
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = lstrlenA(pstrNextArg);
nItemLen = max(1, nItemLen);
}
}
break;
case 's'|FORCE_UNICODE:
case 'S'|FORCE_UNICODE:
{
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = wcslen(pstrNextArg);
nItemLen = max(1, nItemLen);
}
}
break;
}
// adjust nItemLen for strings
if (nItemLen != 0)
{
if (nPrecision != 0)
nItemLen = min(nItemLen, nPrecision);
nItemLen = max(nItemLen, nWidth);
}
else
{
switch (*lpsz)
{
// integers
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
if (nModifier & FORCE_INT64)
va_arg(argList, __int64);
else
va_arg(argList, int);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'e':
case 'g':
case 'G':
va_arg(argList, DOUBLE_ARG);
nItemLen = 128;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'f':
{
double f;
LPTSTR pszTemp;
// 312 == strlen("-1+(309 zeroes).")
// 309 zeroes == max precision of a double
// 6 == adjustment in case precision is not specified,
// which means that the precision defaults to 6
pszTemp = (LPTSTR)_alloca(max(nWidth, 312+nPrecision+6));
f = va_arg(argList, double);
_stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f );
nItemLen = _tcslen(pszTemp);
}
break;
case 'p':
va_arg(argList, void*);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
// no output
case 'n':
va_arg(argList, int*);
break;
default:
ASSERT(FALSE); // unknown formatting option
}
}
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen;
}
TCHAR* szData = new TCHAR[nMaxLen + 1];
VERIFY(_vstprintf(szData, lpszFormat, argListSave) <= nMaxLen);
OutputDebugString(szData);
delete [] szData;
va_end(argListSave);
va_end(argList);
}
EDIT1: Fixed source includes. [edited by - dalleboy on August 5, 2002 1:58:28 PM] |
||||
|
||||
![]() sark Member since: 4/16/2002 |
||||
|
|
||||
| Did anyone read my post above? My solution avoids the 1024 buffer problem, automatically formats numbers and floats etc and you can use the standard iostream manipulators. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
quote:Sure we did, but iostream-like output sux, printf-like output is much easier (especially for us c-programmers). Does anyone <b>really</b> use iostream for output of text in games? |
||||
|
||||
![]() xmoby Member since: 9/12/2002 From: Canada |
||||
|
|
||||
| There may be a little problem with the TRACE macro (I'm not sure...). I know the code is compiled out, but what about the parameters passed to the macro? Are they compiled, taking place in the data segment of the final executable? A good test would be to use a function as a parameter, and to see if it's called in release mode... |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Can somebody tell me what you can do about the assembler code when you use GCC for compiling? Is there a possibility to get this working? |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| rather than using non-portable assembler to implement ASSERT, you could just use the standard assert() macro. This is garanteed to work everywhere, and is portable. |
||||
|
||||
All times are ET (US)![]() |
Last Thread Next Thread ![]() |
|