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

时间:2012-11-28 16:51:36

标签: c++ preprocessor-directive

我正在为我的项目开发一个简单的记录器包装器,这样我就可以轻松地换出后端了。
这是我理想的界面:

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

我实施它的方式是:

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

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

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

  4. Contrived Implementation:

    #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
    

    什么是禁用这些类型的链接调用的简洁方法? 任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:4)

你必须犯下另一个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

这非常简单,可以远离编译器进行优化。

答案 1 :(得分:1)

这不是最漂亮的,但你可以这样做:

#ifdef LOGGING_ENABLED
#define LOG(message) message
#else
#define LOG(message)
#endif


LOG(log << "this" << "is" << "a" << "test");

您可以通过执行此操作来略微简化

#ifdef LOGGING_ENABLED
#define LOG(message) log << message
#else
#define LOG(message)
#endif


LOG("this" << "is" << "a" << "test");

答案 2 :(得分:1)

流方法存在一个问题,即使使用空流:C ++中也没有延迟计算。

即使你的流对参数没有任何作用,参数仍然是完全创建的。

避免此评估的唯一方法是使用宏:

#define LOG(Message_) \
    do (LogManager::activated()) {
        logger << Message_;
    } while(0);

当然,我会注意到,如果你使用一个宏,那么在__func____FILE____LINE__中加入一个很好的机会。