实现CLogClass以进行体面的日志记录我还定义了宏,但它只适用于一个参数......
class CLogClass
{
public:
static void DoLog(LPCTSTR sMessage, ...);
};
#define DebugLog(sMessage, x) ClogClass::DoLog(__FILE__, __LINE__, sMessage, x)
嗯,当用超过2个参数调用它时会失败:( ......它是否可以避免它?它能以某种方式转换为模板吗?
编辑:Variadic宏在VS 2005中引入(但我目前在VS 2003中......)。有什么建议吗?答案 0 :(得分:5)
你可以让一个MYLOG宏返回一个自定义函子对象,它接受可变数量的参数。
#include <string>
#include <cstdarg>
struct CLogObject {
void operator()( const char* pFormat, ... ) const {
printf( "[%s:%d] ", filename.c_str(), linenumber );
va_list args;
va_start( args, pFormat );
vfprintf( stderr, pFormat, args );
va_end( args );
}
CLogObject( std::string filename, const int linenumber )
: filename( filename ), linenumber( linenumber )
{}
std::string filename;
int linenumber;
};
#define MYLOG CLogObject( __FILE__, __LINE__ )
int _tmain(int argc, _TCHAR* argv[])
{
MYLOG( "%s, %d", "string", 5 );
return 0;
}
请注意,切换到this answer触及的类型安全变体并不困难:由于operator<<
的链接效应,您不需要任何可变参数。
struct CTSLogObject {
template< typename T >
std::ostream& operator<<( const T& t ) const {
return std::cout << "[" << filename << ":" << linenumber << "] ";
}
CTSLogObject( std::string filename, const int linenumber )
: filename( filename ), linenumber( linenumber )
{}
std::string filename;
int linenumber;
};
#define typesafelog CTSLogObject( __FILE__, __LINE__ )
int _tmain(int argc, _TCHAR* argv[])
{
typesafelog << "typesafe" << ", " << 5 << std::endl;
return 0;
}
答案 1 :(得分:3)
您的问题实际上有两个答案。您希望执行通用日志记录功能,该功能与printf类似,但可以完全自定义。所以你需要:
以下是您的代码示例adatapted:
#include <stdio.h>
#include <stdarg.h>
class CLogClass
{
public:
static void DoLogWithFileLineInfo( const char * fmt, ... )
{
va_list ap;
va_start( ap, fmt );
vfprintf( stderr, fmt, ap );
va_end( ap );
}
};
#define MYLOG(format, ...) CLogClass::DoLogWithFileLineInfo("%s:%d " format , __FILE__, __LINE__, __VA_ARGS__)
int main()
{
MYLOG("Hello world!\n", 3); // you need at least format + one argument to your macro
MYLOG("%s\n", "Hello world!");
MYLOG("%s %d\n", "Hello world!", 3);
}
C99中引入了可变参数宏,因此它可以在支持C99或C ++ 0x的编译器上运行。我用gcc 3.4.2和Visual Studio 2005成功测试了它。
函数的变量参数一直存在,所以不用担心这里的兼容性。
这可能是通过一些模板元编程来实现的,但考虑到上面代码的简单性,我没有看到它的兴趣。
作为最后一点,为什么在空类而不是函数中使用静态方法?
答案 2 :(得分:1)
class Log {
stringstream buffer;
public:
class Proxy {
public:
Proxy(Log& p) : parent(p) {}
template<class T>
Proxy& operator,(T const& t) {
parent.buffer << t;
return *this;
}
~Proxy() {
parent.buffer << endl;
cout << parent.buffer.str();
parent.buffer.str("");
}
private:
CLog& parent;
};
template<class T>
Proxy operator<<(T const& t) {
buffer << t;
return Proxy(*this);
}
};
可以简单地扩展为写入时间戳,检查loglevel,写入文件等。
或者,更简单但不那么灵活:
class Log {
public:
class Proxy {
public:
template<class T>
Proxy& operator,(T const& t) {
cout << t;
return *this;
}
~Proxy() {
cout << endl;
}
};
template<class T>
Proxy operator<<(T const& t) {
cout << t;
return Proxy();
}
};
用法:
Log log;
void f() {
log << "hey, my age is ", age;
}
答案 3 :(得分:0)
在这个实例中,我倾向于使用全局可见的extern函数而不是宏,并使用va_list解析此函数中的省略号。请参阅我之前发布的example on how to achieve this帖子。