C++ Simple Message/Logging Class

瀟湘夜雪發表於2018-10-22

 

    在 Qt的原始碼與Protobuf 的程式碼中,看到相同的簡單訊息(日誌)輸出的類實現,基本思路是使用巨集定義,過載臨時類物件,呼叫類方法或者通過解構函式自動呼叫輸出方法,實現訊息輸出。這裡以 Protobuf 的LogMessage 類為例,簡單描述實現方法。

    類定義很簡單,主要是建構函式、過載的 operator<< 操作符、Finish方法。建構函式傳入日誌等級、檔名及行號,為輸出用。過載的 << 操作符為了流式輸出。在後邊實現了個私有的  Finish 方法,該方法簡單粗暴,就是呼叫輸出函式,如果是 Fatal 等級輸出,還會丟擲異常或者 abort。其定義如下:

enum LogLevel {
  LOGLEVEL_INFO,     // Informational.  This is never actually used by
                     // libprotobuf.
  LOGLEVEL_WARNING,  // Warns about issues that, although not technically a
                     // problem now, could cause problems in the future.  For
                     // example, a // warning will be printed when parsing a
                     // message that is near the message size limit.
  LOGLEVEL_ERROR,    // An error occurred which should never happen during
                     // normal use.
  LOGLEVEL_FATAL,    // An error occurred from which the library cannot
                     // recover.  This usually indicates a programming error
                     // in the code which calls the library, especially when
                     // compiled in debug mode.
};

class LIBPROTOBUF_EXPORT LogMessage {
 public:
  LogMessage(LogLevel level, const char* filename, int line);
  ~LogMessage();

  LogMessage& operator<<(const std::string& value);
  LogMessage& operator<<(const char* value);
  LogMessage& operator<<(char value);
  LogMessage& operator<<(int value);
  LogMessage& operator<<(uint value);
  LogMessage& operator<<(long value);
  LogMessage& operator<<(unsigned long value);
  LogMessage& operator<<(long long value);
  LogMessage& operator<<(unsigned long long value);
  LogMessage& operator<<(double value);
  LogMessage& operator<<(void* value);
  LogMessage& operator<<(const StringPiece& value);
  LogMessage& operator<<(const ::google::protobuf::util::Status& status);
  LogMessage& operator<<(const uint128& value);

 private:
  friend class LogFinisher;
  void Finish();

  LogLevel level_;
  const char* filename_;
  int line_;
  std::string message_;
};

   

    同時實現了一個 LogFinisher 類,只過載了 operator= 操作符,該方法只是呼叫 LogMessage 的 Finish 方法。

1 class LIBPROTOBUF_EXPORT LogFinisher {
2  public:
3   void operator=(LogMessage& other) {
4     other.Finish();
5   }
6 };

 

    再者實現方便使用的巨集定義,定義如下所示。主要為 GOOGLE_LOG 巨集,該巨集構造 LogMessage 臨時物件,呼叫 LogFinisher 的 operator= 方法,實現訊息輸出。在Qt 的程式碼中,並沒有實現類似  LogFinisher 的類,直接構造了MessageLog 臨時物件,在物件析構時呼叫輸出函式,臨時物件生命週期會在構造完執行析構,執行訊息輸出。以下巨集定義中還定義了 GOOGLE_LOG_IF ,即條件為 true 時,什麼都不做 (void)0,為 false時呼叫 GOOGLE_LOG。

 1 #define GOOGLE_LOG(LEVEL)                                                 
 2   ::google::protobuf::internal::LogFinisher() =                           
 3     ::google::protobuf::internal::LogMessage(                             
 4       ::google::protobuf::LOGLEVEL_##LEVEL, __FILE__, __LINE__)
 5 #define GOOGLE_LOG_IF(LEVEL, CONDITION) 
 6   !(CONDITION) ? (void)0 : GOOGLE_LOG(LEVEL)
 7 
 8 #define GOOGLE_CHECK(EXPRESSION) 
 9   GOOGLE_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": "
10 #define GOOGLE_CHECK_OK(A) GOOGLE_CHECK(::google::protobuf::internal::IsOk(A))
11 #define GOOGLE_CHECK_EQ(A, B) GOOGLE_CHECK((A) == (B))
12 #define GOOGLE_CHECK_NE(A, B) GOOGLE_CHECK((A) != (B))
13 #define GOOGLE_CHECK_LT(A, B) GOOGLE_CHECK((A) <  (B))
14 #define GOOGLE_CHECK_LE(A, B) GOOGLE_CHECK((A) <= (B))
15 #define GOOGLE_CHECK_GT(A, B) GOOGLE_CHECK((A) >  (B))
16 #define GOOGLE_CHECK_GE(A, B) GOOGLE_CHECK((A) >= (B))

    

    通過以上實現,則可以在程式碼中使用以下形式進行訊息輸出或者斷言。第一行直接輸出 “Hello sunshy”,額,sunshy 是寡人姓名拼音的簡拼,第二行在var0 和 var1 不相等時輸出後邊的訊息,同時拋異常或者abort,相等時則 do nothing

1 GOOGLE_LOG(INFO)<< "Hello " <<"sunshy
";
2 GOOGLE_CHECK_EQ(var0, var1)<<" var0 not equal var
";

 

    最新工作的事心有不暢,隨便寫寫,以遣胸懷

 

相關文章