【问题标题】:Is this possible use ellipsis in macro? Can it be converted to template?这可能在宏中使用省略号吗?可以转成模板吗?
【发布时间】:2010-09-29 23:49:19
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: c++ visual-c++ logging c-preprocessor


    【解决方案1】:

    在这种情况下,我倾向于使用全局可见的外部函数而不是宏,并使用 va_list 解决此函数中的省略号。请参阅我以前的帖子以获取 example on how to achieve this

    【讨论】:

    • 这不能满足提问者的愿望,包括 FILELINE 参数。这些只能由宏插入。
    【解决方案2】:

    您的问题实际上有两个答案。您想要执行通用日志记录功能,该功能类似于 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 成功测试了它。

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

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

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

    【讨论】:

    • 所有这些都有效......除了宏之外 - 正如我所说的省略号在 VS2003 中的宏中不起作用。见 - msdn.microsoft.com/en-us/library/ms177415(VS.80).aspx ...
    • 如果不能使用可变参数宏,那就更难了。记得cppjournal的一篇文章,有两个宏的complex trick,我看看能不能再找到。
    【解决方案3】:

    您可以让 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&lt;&lt; 的链接效应,您不需要任何可变参数。

    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;
    }
    

    【讨论】:

    • 这似乎几乎是正确的......但是将属性链接到一个流中不是问题......真正的问题是通过进程边界序列化包含所有参数的类......然后反序列化它们...... . 所以这是一个很好的解决方案,但仍然不是答案......
    • 你在问题​​中提到了序列化的事情吗?看起来好像您需要一种机制来为某些编译时已知的文字添加可变数量的参数。
    【解决方案4】:
    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;
    }
    

    【讨论】:

    • 确实,链式操作的解决方案也可以很好地工作。但是为什么要混合使用“operator
    • 强调语义与ostream的相同。特别是,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 2020-01-11
    相关资源
    最近更新 更多