可変引数リストを取るデバッグ専用関数を作成するにはどうすればよいでしょうか?printf() のように
-
08-06-2019 - |
質問
と同じパラメータを使用してデバッグログ関数を作成したいと考えています printf
. 。ただし、これは最適化されたビルド中にプリプロセッサによって削除できるものです。
例えば:
Debug_Print("Warning: value %d > 3!\n", value);
可変個引数マクロについて調べましたが、すべてのプラットフォームで利用できるわけではありません。 gcc
彼らをサポートし、 msvc
ではない。
解決
私は今でも昔ながらの方法で、no-op または可変引数リストを使用した関数呼び出しに関連するマクロ (XTRACE、以下) を定義しています。内部的に vsnprintf を呼び出して、printf 構文を維持できるようにします。
#include <stdio.h>
void XTrace0(LPCTSTR lpszText)
{
::OutputDebugString(lpszText);
}
void XTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512]; // get rid of this hard-coded buffer
nBuf = _vsnprintf(szBuffer, 511, lpszFormat, args);
::OutputDebugString(szBuffer);
va_end(args);
}
次に、典型的な #ifdef スイッチ:
#ifdef _DEBUG
#define XTRACE XTrace
#else
#define XTRACE
#endif
まあ、それはかなりクリーンにすることができますが、それが基本的な考え方です。
他のヒント
これは、C++ でデバッグ出力を行う方法です。「dout」(デバッグアウト) を次のように定義します。
#ifdef DEBUG
#define dout cout
#else
#define dout 0 && cout
#endif
コードでは、「dout」を「cout」と同じように使用します。
dout << "in foobar with x= " << x << " and y= " << y << '\n';
プリプロセッサが 'dout' を '0 && cout' に置き換える場合、<< の優先順位が && よりも高く、&& の短絡評価により行全体が 0 と評価されることに注意してください。0 が使用されていないため、コンパイラはその行に対してコードをまったく生成しません。
ここでは私が C/C++ でやっていることを示します。まず、varargs を使用する関数を作成します (Stu の投稿のリンクを参照)。次に、次のようなことを実行します。
int debug_printf( const char *fmt, ... );
#if defined( DEBUG )
#define DEBUG_PRINTF(x) debug_printf x
#else
#define DEBUG_PRINTF(x)
#endif
DEBUG_PRINTF(( "Format string that takes %s %s\n", "any number", "of args" ));
覚えておく必要があるのは、デバッグ関数を呼び出すときに二重括弧を使用することだけです。そうすれば、非 DEBUG コードでは行全体が削除されます。
可変個引数関数をスタブアウトするもう 1 つの楽しい方法は次のとおりです。
#define function sizeof
@CodingTheWheel:
あなたのアプローチには少し問題が 1 つあります。次のような呼び出しを考えてみましょう
XTRACE("x=%d", x);
これはデバッグ ビルドでは正常に機能しますが、リリース ビルドでは次のように展開されます。
("x=%d", x);
これは完全に正規の C であり、コンパイルされ、通常は副作用なしで実行されますが、不要なコードが生成されます。この問題を解決するために私が通常使用するアプローチは次のとおりです。
XTrace 関数が int を返すようにします (0 を返すだけで、戻り値は関係ありません)。
#else 句の #define を次のように変更します。
0 && XTrace
リリース バージョンは次のように拡張されます。
0 && XTrace("x=%d", x);
そして、まともなオプティマイザであれば、短絡評価によって && 以降の何も実行されなくなるため、すべてを破棄します。
もちろん、最後の文を書いたときに、おそらく元の形式も最適化されて取り除かれる可能性があり、XTrace にパラメーターとして渡される関数呼び出しなどの副作用の場合には、より良い解決策になる可能性があることに気づきました。デバッグ バージョンとリリース バージョンが同じように動作することを確認してください。
C++ では、ストリーミング演算子を使用して処理を簡素化できます。
#if defined _DEBUG
class Trace
{
public:
static Trace &GetTrace () { static Trace trace; return trace; }
Trace &operator << (int value) { /* output int */ return *this; }
Trace &operator << (short value) { /* output short */ return *this; }
Trace &operator << (Trace &(*function)(Trace &trace)) { return function (*this); }
static Trace &Endl (Trace &trace) { /* write newline and flush output */ return trace; }
// and so on
};
#define TRACE(message) Trace::GetTrace () << message << Trace::Endl
#else
#define TRACE(message)
#endif
そしてそれを次のように使用します:
void Function (int param1, short param2)
{
TRACE ("param1 = " << param1 << ", param2 = " << param2);
}
その後、クラスに出力する場合とほぼ同じ方法で、クラスのカスタマイズされたトレース出力を実装できます。 std::cout
.
ああ、vsprintf() が私に欠けていたものでした。これを使用して、可変引数リストを printf() に直接渡すことができます。
#include <stdarg.h>
#include <stdio.h>
void DBG_PrintImpl(char * format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
printf("%s", buffer);
va_end(args);
}
次に、全体をマクロでラップします。
利用できないプラットフォームは何ですか?stdarg は標準ライブラリの一部です。
http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html
これを提供しないプラットフォームは、標準の C 実装ではありません (または非常に古い)。これらの場合は、可変引数を使用する必要があります。
http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html
この種の機能の問題の一部は、多くの場合、バリアードマクロが必要であることです。これらはかなり最近標準化されており(C99)、多くの古いCコンパイラが標準をサポートしていないか、独自の特別な仕事をしていません。
以下は、いくつかの優れた機能を備えた私が作成したデバッグ ヘッダーです。
- デバッグ マクロの C99 および C89 構文をサポート
- 関数の引数に基づいて出力を有効/無効にする
- ファイル記述子(file io)への出力
注記:何らかの理由で、コードのフォーマットに若干の問題が発生しました。
#ifndef _DEBUG_H_
#define _DEBUG_H_
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "stdarg.h"
#include "stdio.h"
#define ENABLE 1
#define DISABLE 0
extern FILE* debug_fd;
int debug_file_init(char *file);
int debug_file_close(void);
#if HAVE_C99
#define PRINT(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, format, ##__VA_ARGS__); \
} \
else { \
fprintf(stdout, format, ##__VA_ARGS__); \
} \
}
#else
void PRINT(int enable, char *fmt, ...);
#endif
#if _DEBUG
#if HAVE_C99
#define DEBUG(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
else { \
fprintf(stderr, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
}
#define DEBUGPRINT(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, format, ##__VA_ARGS__); \
} \
else { \
fprintf(stderr, format, ##__VA_ARGS__); \
} \
}
#else /* HAVE_C99 */
void DEBUG(int enable, char *fmt, ...);
void DEBUGPRINT(int enable, char *fmt, ...);
#endif /* HAVE_C99 */
#else /* _DEBUG */
#define DEBUG(x, format, ...)
#define DEBUGPRINT(x, format, ...)
#endif /* _DEBUG */
#endif /* _DEBUG_H_ */
今日この問題に遭遇したので、私の解決策は次のマクロです。
static TCHAR __DEBUG_BUF[1024]
#define DLog(fmt, ...) swprintf(__DEBUG_BUF, fmt, ##__VA_ARGS__); OutputDebugString(__DEBUG_BUF)
次に、次のように関数を呼び出すことができます。
int value = 42;
DLog(L"The answer is: %d\n", value);
これが私が使っているものです:
inline void DPRINTF(int level, char *format, ...)
{
# ifdef _DEBUG_LOG
va_list args;
va_start(args, format);
if(debugPrint & level) {
vfprintf(stdout, format, args);
}
va_end(args);
# endif /* _DEBUG_LOG */
}
_DEBUG_LOG フラグがオフになっている場合、実行時にはまったくコストがかかりません。
これはユーザーの回答の TCHAR バージョンであるため、ASCII として機能します (普通)、または Unicode モード (多かれ少なかれ)。
#define DEBUG_OUT( fmt, ...) DEBUG_OUT_TCHAR( \
TEXT(##fmt), ##__VA_ARGS__ )
#define DEBUG_OUT_TCHAR( fmt, ...) \
Trace( TEXT("[DEBUG]") #fmt, \
##__VA_ARGS__ )
void Trace(LPCTSTR format, ...)
{
LPTSTR OutputBuf;
OutputBuf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, \
(size_t)(4096 * sizeof(TCHAR)));
va_list args;
va_start(args, format);
int nBuf;
_vstprintf_s(OutputBuf, 4095, format, args);
::OutputDebugString(OutputBuf);
va_end(args);
LocalFree(OutputBuf); // tyvm @sam shaw
}
「多かれ少なかれ」と言ったのは、ASCII 文字列引数を WCHAR に自動的に変換するわけではないからですが、フォーマット文字列を TEXT() で囲んだり、その前に L を付けたりすることを気にせずに、ほとんどの Unicode スクレイピングから解放されるはずです。 。
主に以下に由来します MSDN:前回のエラーコードの取得