幾天前,在CSDN論壇看到這么一則討論:在宏定義中怎么使用可變參數?(http://expert.csdn.net/Expert/topic/2925/2925165.xml)。樓主希望能定義這樣的macro:
#define fun1(a, b, ...) fun2(__FILE__, __LINE__, a, b, ...)
我猜樓主是想寫trace,如果不能使用可變參數的macro,那么就得像MFC那樣寫一堆TRACE macros:
// 取自 MFC 7.1 的 afx.h
// The following trace macros are provided for backward compatiblity
// (they also take a fixed number of parameters which provides
// some amount of extra error checking)
#define TRACE0(sz) TRACE(_T("%s"), _T(sz))
#define TRACE1(sz, p1) TRACE(_T(sz), p1)
#define TRACE2(sz, p1, p2) TRACE(_T(sz), p1, p2)
#define TRACE3(sz, p1, p2, p3) TRACE(_T(sz), p1, p2, p3)
太丑陋了!還好,C99標準支持Variadic Macros,在GCC中,可以這么寫:
// http://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html
#define debug(format, ...) fprintf(stderr, format, __VA_ARGS__)
還可以順便打印文件名和行號:
#define debug(format, ...) do {/
fprintf(stderr, "%s (%d): ", __FILE__, __LINE__);/
fprintf(stderr, format, __VA_ARGS__);/
} while (0)
但可惜Visual C++ 7.1還不支持這項功能:( 不過我們在C++中至少可以繞彎解決,做到既能自動記錄文件名和行號,又能使用變長參數調用。這個辦法不是我獨創(chuàng)的,實際上ATL的atltrace.h中就有它的實現(CtraceFileAndLineInfo class),我在Code Project也找到了相同的實現(http://www.codeproject.com/debug/location_trace.asp),甚至在CUJ的C++ Experts Forum 也能看到相近的做法(http://www.cuj.com/documents/s=8250/cujcexp2106alexandr/),當然Alexandrescu的辦法技巧性更強。
思路:寫一個重載了 operator() 的class,令 TRACE 宏返回該class的一個object:
#include
#include
#ifndef NDEBUG // debug mode
class tracer
{
public:
tracer(const char* file, int line)
: file_(file), line_(line)
{}
void operator()(const char* fmt, ...)
{
va_list ap;
// print the file name and line number
fprintf(stderr, "%s (%d): ", file_, line_);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "/r/n"); // print the new-line character
}
private:
// copy-ctor and operator=
tracer(const tracer&);
tracer& operator=(const tracer&);
private:
const char* file_;
int line_;
};
#define TRACE (tracer(__FILE__, __LINE__))
#else // NDEBUG
#define TRACE (void)
#endif // NDEBUG
int main()
{
#ifndef NDEBUG
tracer(__FILE__, __LINE__)("%x", 123);
#endif
TRACE("%s", "Happy debugging.");
}
這樣做是multithreading-safe的。G++ 3.3.1 / Visual C++ 7.1 / Borland C++ 5.5.1 通過。