1. 程式人生 > >C++解析頭文件-Qt自動生成信號聲明

C++解析頭文件-Qt自動生成信號聲明

www 用戶操作 .com creat 設計工具 實現 end ren map

目錄

  • 一、瞎白話
  • 二、背景
  • 三、思路分析
  • 四、代碼講解
    • 1、類圖
    • 2、內存結構聲明
    • 3、QtHeaderDescription
    • 4、私有函數講解
  • 五、分析結果
  • 六、下載

一、瞎白話

時間過的ZTMK,距離上一篇文章已經小半年過去了。為了安家、裝修和結婚,搞得自己焦頭爛額,這不是也正好趕上過年,一直沒有時間寫篇文章,最近終於慢慢回歸正軌,所以決定寫下這一篇文章,記錄工作中的一些經驗和內容。對於寫文章這件事,我是這麽認為的:一個是回顧自己的工作內容;另一方面也是為了能讓有同樣需求的同學用於借鑒。同時這也是我對自己的一個要求,每個階段都應該有所輸出,並有所記錄,倘若若幹年後,有機會再次看到這些東西的時候,能有一絲感動。。。

廢話不多說了,那我們直接進入今天要分享的內容,怎麽去自動生成Qt的信號聲明

二、背景

用過Qt的人應該都知道,Qt中的每個控件都有很多信號,基於這些信號,我們可以實現我們自己的響應函數,也就是槽函數,通常槽函數和信號是通過connect連接起來的。除此之外呢,還有一種書寫槽的方式,我們可以不用connect來連接,那就是我們的槽函數名稱需要滿足一定的規律,比如我們要實現一個名稱為pushButton_ok的按鈕點擊事件,那麽我們的槽函數聲明可能會像下面這樣:

private slots:
    void on_pushButton_ok_clicked();

用過QtCreator寫代碼的人可能都知道,上述的代碼可以通過QtCreator

內嵌的界面設計工具直接生成,但是當我們直接用QtDesigner工具編輯時,沒有了轉到槽這個菜單,如下圖左側的截圖所示
技術分享圖片

上圖中的右側截圖是我們修改過後的截圖,當我們直接編輯UI文件時,也可以轉到槽,不同的是我們需要自己去相應的.h和.cpp文件中去把聲明和實現添加上,本篇文章我們先分析怎麽添加函數聲明,下篇文章在分析怎麽添加函數實現定義,函數聲明和定義是怎麽構造的,這裏不會講解,Qt源碼中都有,有興趣的同學可以自己去了解下。

三、思路分析

既然我們的函數聲明已經有了,我們只需要打開ui文件對應的頭文件,然後把代碼插入到合適的位置上即可,這裏有2種方式實現。

  • 方式一:直接查找指定類的指定作用域標識符,插入到標識符之後
  • 方式二:分析頭文件,解析類和其他有用信息,在內存中把類描述出來,插入時更靈活

以上兩種方式,各有利弊,第一種方式簡單粗暴,比較容易實現功能,但擴展性差,比如說要插入到指定域的所有函數之後,就比較難;第二種方式實現起來比較復雜,解析頭文件是一個比較大的活,但是一旦文件解析成功後,插入工作就變得很簡單。這裏我選擇了第二種方式來實現這個功能。

首先,解析頭文件,我畫了一個大致的流程圖,主要是為了理解起來方便,並不是特別專業,湊合著看下
技術分享圖片
頭文件解析時,主要的規則還是按行讀取代碼,然後去檢測是否滿足某一個類型條件,比如說已\\開頭的我們認為是註釋。

當滿足條件時,我們去更新相應的內存結構,然後繼續往下讀,有時候我們可能需要連續讀取好幾行才能知道當前的內容是什麽,

class 
    A
        ;

如上述代碼所示,是一個不標準的C++類預聲明,我們只有讀到;時,才知道這是一個類預聲明,而不是一個類聲明,有點兒繞口,但是這個很重要。

圖中對於解析一個類模塊,只是簡單的用了一個塊來表示,實際上解析一個類也是比較費勁的。當我們解析完類文件之後,就是簡單的插入操作了,插入流程如下圖所示
技術分享圖片

四、代碼講解

1、類圖

類圖中總共有3個模塊:對外暴露的QtGrammaAnalysis類,提供給用戶操作;QtFileCache是文件緩存類,供QtGrammaAnalysis類調用,文件緩存類可以有多個,主要是為了分析不同類型的文件;QtHeaderDescription是真正的文件描述類,所有的實際操作都是通過這個類來進行的。

技術分享圖片

2、內存結構聲明

哈哈哈,好的代碼自帶註釋,下面的結構體是為了我們解析頭文件而聲明的,每個重要的字段都有註釋,這裏不在做解釋

struct OffsetItem
{
    int start;//偏移起始行
    int number;//偏移大小
};

struct BaseItem
{
    BaseItem() :start(0), end(0) {}
    QString name;//項目名稱  列如註釋內容、塊名稱等
    int start;//標記代碼所在起始行  
    int end;//標記代碼所在結束行

    BaseItem & BaseItem::operator += (const OffsetItem &);
};

struct ScopePiece : public BaseItem
{
    ScopePiece() :g_end(-1) {}
    int g_end;//作用域下所有代碼結束
    QList<BaseItem> funcations;//函數(變量)列表

    ScopePiece & ScopePiece::operator += (const OffsetItem & offset);
};


struct ClassDescription : public BaseItem
{
    ClassDescription() :g_end(-1) {}
    int g_end;//作用域下所有代碼結束
    QList<QString> parents;//父類
    QMap<int, ScopePiece> pieces;//作用域列表  行號:域
    QMap<int, ScopePiece> pieceIndexs;//快速訪問索引 域類型:域

    void RowNumber(const OffsetItem &);
};

struct HeaderFile
{
    QString name;//文件名
    QList<BaseItem> note;//註釋
    QList<BaseItem> macros;//宏定義
    QList<BaseItem> includeHeader;//包含頭文件
    QList<BaseItem> predeclaration;//類預聲明
    QMap<QString, ClassDescription> classDeclare; //類列表
    QMap<int, QString> classOrder; //類順序
    QList<BaseItem> cStyleFuncations;//C函數(全局變量)

    void CleanUp()
    {
        note.clear();
        macros.clear();
        includeHeader.clear();
        predeclaration.clear();
        classDeclare.clear();
        cStyleFuncations.clear();
    }

    void RowNumber(int, int);
};

當我們把程序運行起來後,解析一個類文件時,他的內存描述可能會像這樣
技術分享圖片

3、QtHeaderDescription

QtHeaderDescription是解析頭文件的真正實現類,代碼比較多,這了我講下每個函數聲明的作用

void SetFile(const QString &); 設置頭文件
void Refrush(); 刷新內存結構
void CleanUp(bool = true); 清空內存結構
void GenerateFuncationCode(FuncType, const QString &, const QString & = ""); 插入指定代碼在某個作用域
int GetClassStart(const QString & = "") const; 獲取類的開始行
int GetClassEnd(const QString & = "") const; 獲取類的結束行
int GetScopePieceStart(FuncType, const QString & = "") const; 獲取作用域的開始行
int GetScopePieceEnd(FuncType, const QString & = "") const;
void DeleteRow(int); 獲取作用域的結束行
QString GetDefaultClass() const { return  m_strDefaultClass; }獲取默認的插入類名稱
void Save(); 保存新的文件

4、私有函數講解

StatementType GuessType(int); 預測當前行類型,可能是註釋、類或者函數等
void ReadFile(); 讀取一個文件到內存
void AnalysisFile(); 分析內存中的文件到指定結構中
void AnalysisOne(int &); 分析一行代碼
void ReadSingleRow(int); 插入到內存中
void ReadMutilRows(int, int); 插入多行到內存中
void ReadClass(int, int); 讀取一個類
void AnalysisClass(int &); 分析一行代碼(在類中)
void ReadClassRows(int, int); 插入多行到內存(在類中)
void ReadClassScope(const BaseItem &); 插入域(在類中)
void ReadClassFuncation(const BaseItem &); 插入函數(在類中)
void ReadClassEnd(int, int); 更新類結束標致
QString GenerateString(int start, int end); 根據行號生成串

五、分析結果

QtGrammaAnalysis analysis;
QString oldFilePath = fileInfo.absoluteFilePath();
analysis.SetHeaderFile(oldFilePath);

analysis.GenerateDeclaration("\tvoid test1();");
analysis.SetScopeType(FT_PROTECT_SLOT);
analysis.GenerateDeclaration("\tvoid test1_1();");

analysis.SetScopeType(FT_PUBLIC_SLOT);
analysis.GenerateDeclaration("\tvoid test1_2();");

analysis.Save();

執行如上插入操作後,如下圖所示
技術分享圖片

六、下載

代碼下載地址:C++解析頭文件-Qt自動生成信號聲明




轉載聲明:本站文章無特別說明,皆為原創,版權所有,轉載請註明:朝十晚八 or Twowords


C++解析頭文件-Qt自動生成信號聲明