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

1、瞎白話

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

廢話很少說了,那咱們直接進入今天要分享的內容,怎麼去自動生成Qt的信號聲明工具

2、背景

用過Qt的人應該都知道,Qt中的每一個控件都有不少信號,基於這些信號,咱們能夠實現咱們本身的響應函數,也就是槽函數,一般槽函數和信號是經過connect鏈接起來的。除此以外呢,還有一種書寫槽的方式,咱們能夠不用connect來鏈接,那就是咱們的槽函數名稱須要知足必定的規律,好比咱們要實現一個名稱爲pushButton_ok的按鈕點擊事件,那麼咱們的槽函數聲明可能會像下面這樣:ui

private slots:
    void on_pushButton_ok_clicked();

用過QtCreator寫代碼的人可能都知道,上述的代碼能夠經過QtCreator內嵌的界面設計工具直接生成,可是當咱們直接用QtDesigner工具編輯時,沒有了轉到槽這個菜單,以下圖左側的截圖所示
image.net

上圖中的右側截圖是咱們修改事後的截圖,當咱們直接編輯UI文件時,也能夠轉到槽,不一樣的是咱們須要本身去相應的.h和.cpp文件中去把聲明和實現添加上,本篇文章咱們先分析怎麼添加函數聲明,下篇文章在分析怎麼添加函數實現定義,函數聲明和定義是怎麼構造的,這裏不會講解,Qt源碼中都有,有興趣的同窗能夠本身去了解下。設計

3、思路分析

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

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

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

首先,解析頭文件,我畫了一個大體的流程圖,主要是爲了理解起來方便,並非特別專業,湊合着看下
image
頭文件解析時,主要的規則仍是按行讀取代碼,而後去檢測是否知足某一個類型條件,好比說已\\開頭的咱們認爲是註釋。索引

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

class 
    A
        ;

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

圖中對於解析一個類模塊,只是簡單的用了一個塊來表示,實際上解析一個類也是比較費勁的。當咱們解析完類文件以後,就是簡單的插入操做了,插入流程以下圖所示
image

4、代碼講解

一、類圖

類圖中總共有3個模塊:對外暴露的QtGrammaAnalysis類,提供給用戶操做;QtFileCache是文件緩存類,供QtGrammaAnalysis類調用,文件緩存類能夠有多個,主要是爲了分析不一樣類型的文件;QtHeaderDescription是真正的文件描述類,全部的實際操做都是經過這個類來進行的。

image

二、內存結構聲明

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

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);
};

當咱們把程序運行起來後,解析一個類文件時,他的內存描述可能會像這樣
image

三、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(); 保存新的文件

四、私有函數講解

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); 根據行號生成串

5、分析結果

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();

執行如上插入操做後,以下圖所示
image

6、下載

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




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

相關文章
相關標籤/搜索