簡體   English   中英

如何在C ++中記錄用戶定義的POD結構

[英]How to log user defined POD struct in C++

我需要將日志記錄添加到遺留c ++項目,該項目包含數百個用戶定義的結構/類。 這些結構只包含主要類型,如intfloatchar[]enum

只要可以重建對象,就需要記錄對象的內容,以人類可讀的方式進行記錄,但不是必須的。

不是為每個類編寫不同的序列化方法,還有其他方法嗎?

由於C ++沒有反射,因此無法在運行時動態檢查對象的成員。 因此,您需要為每種類型編寫特定的序列化/流/日志記錄功能。

如果所有不同類型的成員具有相同的名稱,那么您可以編寫一個模板函數來處理它們,但我認為情況並非如此。

由於C ++沒有反射,因此並不容易。 如果要避免使用詳細解決方案,可以使用可變參數模板。

例如`class MyStruct {private:int a; 漂浮f;

public:void log(){log_fields(a,f); }

其中log_fields()是可變參數模板。 它需要專門針對那些用戶定義類型上的所有基本類型以及遞歸案例。

你想要的是一個程序轉換系統(PTS) 這些工具可以讀取源代碼,構建代表源代碼的編譯器數據結構(通常是AST),並允許您修改AST並從修改后的AST重新生成源代碼。

這些是有用的,因為它們“走出”語言,因此對你可以分析或轉換的內容沒有語言限制。 所以,如果你的語言沒有反映一切,那就無所謂了; 一個好的PTS將讓您完全訪問該語言的每個細節,包括注釋和數字文字的基數。

一些PTS特定於目標語言(例如,“Jackpot”僅可用於Java)。 一個非常好的PTS提供了任意編程語言的描述,然后可以操縱該語言。 該描述必須使PTS能夠解析代碼,分析它(至少構建符號表)並對解析/修改的結果進行漂亮打印。

好的PTS將允許您使用源到源轉換編寫您想要進行的修改。 這些規則指定了大致以下形式編寫的更改:

   if you see *this*, replace it by *that* when *condition*

其中這個 那個是使用目標語言的語法被處理模式 ,以及條件是謂詞(測試)必須為真的以使得能夠應用該規則。 模式表示格式良好的代碼fragmens,並且通常允許元變量表示任意子片段的占位符。

您可以將PTS用於各種程序操作任務。 對於OP的情況,他想要的是枚舉程序中的所有結構,挑選出感興趣的子集,然后為每個選定的結構生成一個序列化程序,作為對原始程序的修改。

為了實現這一特定任務,PTS必須能夠解析並命名解析(構建符號表)C ++。 很少有工具可以做到這一點:Clang,我們的DMS軟件再造工具包和Rose編譯器。

使用DMS的解決方案如下所示:

domain Cpp~GCC5;  -- specify the language and specific dialect to process

pattern log_members( m: member_declarations ): statements = TAG;
      -- declares a marker we can place on a subtree of struct member declarations

rule serialize_typedef_struct(s: statement, m: member_declarations, i: identifier):
           statements->statements
   = "typedef struct { \m } \i;" -> 
     "typedef struct { \m } \i;
      void \make_derived_name\(serialize,\i) ( *\i argument, s: stream )
          { s << "logging" << \toString\(\i\);
            \log_members\(\m\)
          }"
      if selected(i); -- make sure we want to serialize this one

rule generate_member_log_list(m: member_declarations, t: type_specification, n: identifier): statements -> statements
   " \log_members\(\t \n; \m\)" -> " s << \n; \log_members\(\m\) ";

rule generate_member_log_base(t: type_specification, n: identifier): statements -> statements
   " \log_members\(\t \n; \)" -> " s << \n; ";

ruleset generate_logging {
   serialize_typedef struct,
   generate_member_log_list,
   generate_member_log_base 
}

聲明告訴DMS使用哪種特定語言前端。 是的,GCC5作為方言與VisualStudio2013不同,DMS也可以處理。

模式 log_members用作一種轉換指針,記住有一些工作要做。 它將一系列struct member_declarations包裝為議程( 標記 )。 規則的作用是首先使用log_members標記感興趣的結構,以確定生成日志記錄代碼的需要,然后生成成員日志記錄操作。 log_members模式充當列表; 它一次處理一個元素,直到處理完最終元素,然后log_members標記消失,達到了它的目的。

規則 serialize_typedef_struct主要用於掃描代碼以查找要序列化的合適結構。 當它找到一個struct的typedef時,它會檢查該struct是否是一個OP想要序列化的結構(否則就可以省略if條件)。 選擇的元功能自定義編碼的(這里沒有顯示)承認的利益結構的名稱。 當找到合適的typedef語句時,它將被typedef(因此保留它)替換,並由包含議程項log_members的序列化例程的shell替換,該log_members包含結構的整個成員列表。 (如果代碼以某種其他方式聲明結構,例如,作為 ,則需要額外的規則來識別這些情況的語法)。 通過重復重寫來處理議程項目會為各個成員生成日志操作。

規則用DMS規則語法編寫; C ++模式寫在metaquotes “...”中,以使DMS能夠區分規則語法和C ++語法。 占位符變量v根據句法類別在規則標題中聲明,並使用轉義符號\\ v顯示在元引用的模式中。 [注意所選函數調用中未轉義的i :它不在metaquotes中]。 類似地,元引用內部的元函數和模式引用也被類似地轉義,因此最初奇怪的是\\ log \\(... \\)包括轉義的模式名稱和轉義的元括號。

兩個規則generate_member_log_xxx處理日志生成的一般和最終情況。 一般情況處理一個成員要做更多成員; 最后一個案例處理最后一個成員。 (稍有不同的變體是通過重寫到普通的空語句來處理空成員列表; )。 這基本上是一個列表,直到你結束。 我們“欺騙”並編寫相當簡單的日志代碼,依靠流寫入的重載來處理OP聲稱他擁有的不同數據類型。 如果他有更復雜的類型需要特殊處理(例如, 指向... ),他可能想要編寫識別這些情況並生成不同代碼的專門規則。

規則集 generate_logging將這些規則打包成一個整齊的包。 您可以簡單地要求DMS在整個文件上運行此規則集,應用規則直到無法進一步應用規則。 serialize_typdef_structure規則查找感興趣的結構,生成序列化函數shell和log_members議程項,這些項重復重寫以生成成員的序列化。

這是基本的想法。 我還沒有測試過這段代碼,並且通常會有一些令人驚訝的語法變化,你最終必須處理它們,這意味着在同一行上再寫一些規則。

但是一旦實現,您可以在代碼上運行此規則以獲取序列化結果。 (可以實現選擇拒絕已經具有序列化例程的命名結構,或者添加用新生成的代碼替換任何現有序列化代碼的規則,確保序列化過程始終與結構定義匹配)。 生成序列化結構閱讀器有明顯的擴展。

你可以用Clang和/或Rose編譯器來實現這些相同的想法 但是,這些系統不提供源到源重寫規則,因此您必須編寫程序代碼來爬上樹和樹,檢查單個節點等。這是恕我直言,更多的工作和更少的可讀性。

當你遇到下一個“C ++沒有反映出來”時,你可以使用同一個工具解決問題: - }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM