2011年9月10日土曜日

デバッグ用マクロ (C言語)

デバッグ時のみ有効なエラー出力用マクロ。何となくWindows以外でも動きそうな感じに書いてみたけど、実際に使っているのはWindowsだけ。他のOSは試してないので動かないかもれない。(→追記:一応FreeBSD gcc4.2でそれっぽく動いたのでとりあえず良しとする。)

使い方は共通のヘッダファイルに下を書いておいて、それをincludeして
debug("ProcessId=%d",GetCurrentProcessId());
とか書けば
[D] test.cpp[55] WinMain[0]: ProcessId=6508
みたいにメッセージと一緒にソースコードのファイル名や行数、関数名、エラーコードをコンソールに出力してくれる。
GOTO_Eと書くと、メッセージを吐いてからERROR_HANDLERという名前のラベルにgotoする。
#ifndef _LOG_H
#define _LOG_H

#include <stdio.h>

#ifdef _WIN32
#include <windows.h>
#define ERRCD (int)GetLastError()
#define PATH_DELIM '\\'
#else
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#define ERRCD errno
#define PATH_DELIM '/'
#endif

#ifdef _WIN32
// Ctrl-Cとかでコンソールウインドウを閉じなくする。
inline BOOL WINAPI HandlerRoutine(DWORD type)
{
switch(type)
{
case CTRL_C_EVENT: //Ctrl+C
case CTRL_BREAK_EVENT: //Ctrl+Break
case CTRL_CLOSE_EVENT: //CLOSE
return TRUE;
case CTRL_LOGOFF_EVENT: //LOGOFF
case CTRL_SHUTDOWN_EVENT: //SHUTDOWN
default:
return FALSE;
}
}
#endif

inline void debuglog(const int level, const char *file, const char* func, const int line, const int errcd, const char *fmt, ...)
{
#ifdef _WIN32
static bool hasConsole = false;

if(!hasConsole) {
HWND hWnd = GetConsoleWindow();
if(hWnd == NULL) {
FILE *fp;
AllocConsole();
SetConsoleTitle((LPCSTR)"Debug");
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
freopen_s(&fp, "CONOUT$", "w", stderr);
hasConsole = true;

// 「閉じる」をボタン、右クリックメニューから削除。
// コンソールウインドウを閉じられるとプロセスが終了されるのを防げないため。
hWnd = GetConsoleWindow();
HMENU hMenu = GetSystemMenu (hWnd, FALSE);
HINSTANCE hinstance = (HINSTANCE) GetWindowLongPtr (hWnd, GWLP_HINSTANCE);
DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
DrawMenuBar(hWnd);
}
}
#endif
static char* table[] = {"[D]","[I]","[W]","[E]","[A]"};
#pragma warning(push)
#pragma warning(disable:4996)
static int limitlevel = getenv("loglevel")==NULL?0:atoi(getenv("loglevel"));
#pragma warning(pop)

if(level < limitlevel) return;

// フルパスだったらファイル名に
const char* fname = strrchr(file, PATH_DELIM);
if(fname == NULL)
fname = file;
else
fname++;

va_list argp;
fprintf(stderr, "%s %s[%d] %s[%d]: ", table[level], fname, line, func, errcd);
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
fprintf(stderr, "\n");
}

#ifdef _DEBUG
#define debug(...) debuglog(0, __FILE__, __FUNCTION__, __LINE__, ERRCD, __VA_ARGS__);
#define info(...) debuglog(1, __FILE__, __FUNCTION__, __LINE__, ERRCD, __VA_ARGS__);
#define warn(...) debuglog(2, __FILE__, __FUNCTION__, __LINE__, ERRCD, __VA_ARGS__);
#define error(...) debuglog(3, __FILE__, __FUNCTION__, __LINE__, ERRCD, __VA_ARGS__);
#define critical(...) debuglog(4, __FILE__, __FUNCTION__, __LINE__, ERRCD, __VA_ARGS__);

#define GOTO_E {error("[ERROR] goto FUNC_END"); goto FUNC_END;}
#else

#define debug(...)
#define info(...)
#define warn(...)
#define error(...)
#define critical(...)
#define GOTO_E goto FUNC_END
#endif
#endif

0 件のコメント:

コメントを投稿