C++为数据记录定义宏

C++ #define macro for data logging

本文关键字:定义 记录 数据 C++      更新时间:2023-10-16

我有一个vlog类,负责打印程序中的所有日志消息。每个类都可以包括vlog.h头,然后使用以下代码记录数据:

{
    std::lock_guard<std::mutex> lock(vlog::lock_log);
    vlog::EVENT(host) << "hello" << " world!" << std::flush;
}

我想把上面的代码改成一个简单的宏,如下所示:

EVENT_LOG_C(host) << "hello" << " world!" << std::flush;

宏应该首先调用lock_guard,然后将消息传递给vlog::EVENT(host)对象。如何定义宏?

编辑:我希望整个日志消息(可能由多个<<组成)是原子的。每次使用<<都会调用静态对象,我不能将lock_guard放在vlog类本身中。

只需在宏中填充std::lock_guard,但将其作为日志表达式的一部分:

#define EVENT_LOG_C(host) 
    std::lock_guard<std::mutex>{vlog::lock_log}, vlog::EVENT(host)

其中的逗号运算符确保在右侧发生任何事情之前构造临时锁,并且它将一直存在到末尾的分号。


您还可以通过让第一个operator <<返回一个锁定代理来消除宏,然后该代理转发接下来的每个<<,直到表达式结束。

// Simple wrapper around the std::lock_guard to ease operator overloading
struct EventLogLocker {
    EventLogLocker(std::mutex &mtx)
    : lck{mtx} { }
    std::lock_guard<std::mutex> lck;
};
// Perfect-forwarding operator << that hooks on the EventLogLocker
template <class T>
EventLogLocker const &operator << (EventLogLocker const &ell, T &&obj) {
    // Log here
    std::cout << std::forward<T>(obj);
    return ell;
}
// operator << for iostream manipulators (these can't be deduced by the
// template above because they're overloaded for different stream types)
EventLogLocker const &operator << (
    EventLogLocker const &ell,
    std::ostream &(*manip)(std::ostream &)
) {
    std::cout << manip;
    return ell;
}
// Stub function, the important thing is to return an EventLogLocker by value
EventLogLocker EVENT_LOG_C(...) {
    return {lock_log};
}

您所需要做的就是将互斥锁锁定在支持流操作的类的构造函数中,并在析构函数中释放互斥。

不需要宏。

例如;

  namespace vlog
  {
       class EVENT
       {
          public:
           EVENT() : event_lock(vlog::lock_log)
           {
           };
           template<class T> EVENT &operator<<(const T &v)
           {
               std::cerr << v;
               return *this;
           };            
           private:
              std::lock_guard<std::mutex> event_lock;
       };
  }

在上面的讨论中,为了便于讨论,我假设您的日志记录将是std::cerr,并且EVENT的构造函数不接受任何参数。这很容易改变。

那么你所需要做的就是

   vlog::EVENT() << "hello" << "world" << std::flush;

该语句的作用是首先构造vlog::EVENT。构造函数获取互斥对象。然后,在每次流操作期间,互斥对象将继续被捕获。在语句(实际上是其中的表达式)结束时,vlog::EVENT对象将被销毁,在销毁过程中,锁将被释放。

如果您想在多个语句上锁定互斥对象,只需将临时对象绑定到引用即可。将对象的生存期附加到引用的生存期。或者构造一个命名对象。