2012-11-28 63 views
2

我正在爲我的項目工作一個簡單的記錄器包裝,這將允許我輕鬆地交換出後端。
這是我的理想接口:C++禁用鏈接調用,而不包裝在指令

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 

禁用這些類型的鏈接調用的乾淨方式是什麼? 任何幫助表示讚賞。

回答

4

簡化稍微你必須imlement另一個Sink記錄時什麼都不做。考勤記錄稱此爲一個空流:

// 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 

這是非常簡單的,可以從優化編譯器了。

+0

太棒了,但是虛擬'Sink'的拷貝構造函數無法刪除,或者需要移動構造函數。 –

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"); 
1

流方法有一個問題,即使有一個空流:在C++中沒有惰性計算。

也就是說,即使您的流沒有對參數進行任何操作,參數仍然完全創建。

避免這種評估的唯一方法是使用宏:

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

當然,我要指出的是,如果你使用宏,它在__func____FILE____LINE__線程的好該死的機會。