4

在实现了 CLogClass 以进行体面的日志记录之后,我还定义了宏,但它只适用于一个参数......

class CLogClass
{ 
public:
       static void DoLog(LPCTSTR sMessage, ...);
};
#define DebugLog(sMessage, x) ClogClass::DoLog(__FILE__, __LINE__, sMessage, x)

好吧,当使用超过 2 个参数调用时它会失败 :( ... 是否有可能避免它?可以以某种方式将其转换为模板吗?

编辑:可变参数宏是在 VS 2005 中引入的(但我目前在 VS 2003 中......)。有什么建议吗?

4

4 回答 4

6

您可以让 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;
}

请注意,跨步到此答案涉及的类型安全变体并不难:由于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;
}
于 2009-01-07T10:05:19.843 回答
4

您的问题实际上吸引了两个答案。您想要执行通用日志记录功能,该功能类似于 printf 但可以完全自定义。所以你需要:

  • 采用可变数量参数的宏
  • 接受可变数量参数的函数

这是您的代码示例:

#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 成功测试了它。

函数的可变参数一直存在,所以这里不用担心兼容性。

使用一些模板元编程可能可以做到这一点,但鉴于上面代码的简单性,我看不出它的兴趣。

最后一点,为什么在空类中使用静态方法而不是函数?

于 2009-01-07T08:54:16.897 回答
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);
        }
};

可以简单地扩展为写入时间戳、检查日志级别、写入文件等。

或者,更简单但不太灵活:

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;
}
于 2009-01-07T10:13:25.053 回答
0

在这种情况下,我倾向于使用全局可见的外部函数而不是宏,并使用 va_list 解决此函数中的省略号。有关如何实现此目的的示例,请参见我以前的帖子。

于 2009-01-07T08:41:39.670 回答