C++禁用链式调用而不包装指令

标签 c++ preprocessor-directive

我正在为我的项目开发一个简单的记录器包装器,它可以让我轻松更换后端。
这是我理想中的界面:

log::error << "some" << " log " << "message";

我的实现方式是:

  1. log::error#operator<<返回临时 Sink对象。

  2. Sink#operator<<返回 *this并定义一个移动构造函数。

  3. 完整的消息可以在Sink中使用的析构函数,在调用链的末尾调用。

人为实现:

#include <iostream>
#include <string>

struct Sink {

  Sink (std::string const& msg) : m_message(msg) {}

  // no copying
  Sink (Sink const& orig) = delete;

  // move constructor
  Sink (Sink && orig) : m_message(std::move(orig.m_message)) {};

  // use the complete string in the destructor
  ~Sink() { std::cerr << m_message << std::endl;}

  Sink operator<< (std::string const& msg) {
    m_message.append(msg);
    return std::move(*this);
  }

  std::string m_message;
};

struct Level {
  Sink operator<< (std::string const& msg) { return Sink(msg); }
};

int main() {
  Level log;

  log << "this" << " is " << "a " << "test";
}

除了我需要一种干净的禁用日志记录的方法外,这工作正常。 如果我没有使用链接,我的日志函数可以使用预处理器指令来删除函数的内容

void log (std::string) {
  #ifdef LOGGING_ENABLED
    // log message
  #endif
}

然后编译器会优化并删除空函数调用。但是我不知道如何使用我想要实现的 api 来做到这一点。我知道这是可能的,因为 glog以某种方式做到这一点。

使用这样的指令违背了拥有一个好的 api 的目的。

#ifdef LOGGING_ENABLED
  log << "this" << " is " << "a " << "test";
#endif

禁用这些类型的链接调用的干净方法是什么? 感谢您的帮助。

最佳答案

您必须实现另一个 Sink,它在记录日志时不执行任何操作。 Glog 称之为空流:

// A class for which we define operator<<, which does nothing.
class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream {
 public:
  // Initialize the LogStream so the messages can be written somewhere
  // (they'll never be actually displayed). This will be needed if a
  // NullStream& is implicitly converted to LogStream&, in which case
  // the overloaded NullStream::operator<< will not be invoked.
  NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { }
  NullStream(const char* /*file*/, int /*line*/,
             const CheckOpString& /*result*/) :
      LogMessage::LogStream(message_buffer_, 1, 0) { }
  NullStream &stream() { return *this; }
 private:
  // A very short buffer for messages (which we discard anyway). This
  // will be needed if NullStream& converted to LogStream& (e.g. as a
  // result of a conditional expression).
  char message_buffer_[2];
};

// Do nothing. This operator is inline, allowing the message to be
// compiled away. The message will not be compiled away if we do
// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when
// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly
// converted to LogStream and the message will be computed and then
// quietly discarded.
template<class T>
inline NullStream& operator<<(NullStream &str, const T &) { return str; }

在您的情况下,一个简单的实现看起来像

#ifdef LOGGING_ENABLED
  /* your sink */
#else
  struct Sink {
    Sink (std::string)  {}
    Sink (Sink const& orig) {};
  };
  template <typename T> Sink operator<<(Sink s, T) { return s; }
#endif

这非常简单,可以在远离编译器的情况下进行优化。

关于C++禁用链式调用而不包装指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13610256/

相关文章:

javascript - 如何将 SAFEARRAY(字节数组)放入 HTML 隐藏字段

c++ - 如何使用 WinDbg 分析 VC++ 应用程序的故障转储?

c++ - 如何在C++中将多个文件包装到同一个命名空间中

ios - 当且仅当运行时到达代码块内部时才导入类

C宏和整数算术

c++ - 根据其概率选择一个矩阵单元

c++ - 处理动态数据结构时代码中的神秘错误

c - 有没有办法在预处理条件中使用 sizeof ?

ios - 用于 'if not' 的 Objective-C 预处理器指令

c++ - Visual Studio 2013 迁移 : Cannot open include file: 'dxerr8.h' : No such file or directory