一、概述
上一篇文章C++解析頭文件-Qt自動生成信號聲明我們主要講解了怎么去解析C++頭文件,然后在指定位置插入函數聲明,已達到自動化的效果。既然函數聲明已經自動插入了,那么函數實現的定義當然也可以做到自動化插入,而且實現起來比頭文件插入還要簡單,稍微思考下就能得出結論,.cpp
文件的復雜程度要遠遠低於.h
文件。
本篇文章我們就來分享下怎么去插入函數定義
二、實現思路
頭文件插入函數聲明時,我們選擇了先分析頭文件,並記住關鍵位置的行號,然后在指定位置插入字符串,本篇文章我們還是沿用之前的套路,先分析.cpp
文件中的所有函數,並記錄在內存中,當我們插入新的函數時,我們只需要找到我們要插到哪個函數的下邊,或者插入到所有函數之下的行號,執行插入操作即可。
這里有一個很重要的問題,我們怎么知道上一個函數是誰?這個就是函數定義插入的關鍵所在。
答案:通過分析頭文件后的內存結構來獲取。既然我們分析了頭文件中所有的類,而且類中的函數和作用域我們都有存儲,並且記錄了所有的行號,那我們只需要找到指定作用域(或者上一個作用)的最后一個函數即可,這就是我們要插入的位置,當我們在實現文件中尋找行號時,只需要找到這個函數的最后一個行即可。
本篇文章是繼上一篇文章C++解析頭文件-Qt自動生成信號聲明的后續,本篇文章的代碼也是前一篇博客的完善,而且結構有了一定的修改。
三、代碼講解
1、類圖
講述代碼之前,我們先把修改過后的類圖貼上,更簡潔。
- QtGrammaAnalusis:對外接口類
- QtDescription:頭文件分析類基類,提供一些基礎操作,包含了一個自己的指針,主要是頭文件QtHeaderDescription和QtCppDescription可以相互訪問
- QtHeaderDescription:頭文件分析類
- QtCppDescription:實現文件分析類
2、QtCppDescription
本篇文章的細節代碼比較少,因此這里我們稍微講細一點兒,把關鍵代碼都貼上來。首先看下頭文件
a、類定義
看下接口是不是相當簡單,公有接口是對外暴露的,私有函數主要是解析實現文件,下面讓我們看下幾個比較重要的函數
class QtCppDescription : public QtDescription
{
Q_OBJECT
public:
QtCppDescription(QObject * parent);
~QtCppDescription();
public:
virtual void GenerateFuncationCode(FuncType, const QString &, const QString & = "") override;
protected:
virtual void AnalysisFile() override;
private:
StatementType GuessType(int);
void AnalysisOne(int &);
void AnalysisFunc(int &);
private:
QList<BaseItem> m_funcations;//函數(變量)列表
};
b、分析一個完整的函數
代碼分析中,當出現()*{這樣的字符串時,我們認為是要匹配一個函數了,實際上也確實如此。
當我們開始匹配函數時,一個函數的結束也就是,函數中的左右花括號成對出現時
如下代碼所示,當我們匹配完一個函數時,我們構造了一個BaseItem結構來存儲它,主要是記錄了開始行號、結束行號和函數名稱,並存儲在m_funcations結構中
void QtCppDescription::AnalysisFunc(int & row)
{
QString code = GenerateString(m_iCurRow, row);
int rIndex = code.indexOf("(");
int lIndex = code.lastIndexOf("::", rIndex);
QString name = code.mid(lIndex + 2, rIndex - lIndex - 2);
++row;
while (row < m_strRowContexts.size())
{
QString funcCode = GenerateString(m_iCurRow, row);
if (funcCode.count("{") == funcCode.count("}"))
{
BaseItem item;
item.start = m_iCurRow;
item.end = row;
item.name = name;
m_funcations.append(item);
break;
}
++row;
}
}
記錄函數末尾行號主要是為了我們方便插入新代碼。
c、插入代碼
插入函數定義代碼也比較簡單,首先就是根據頭文件分析后的類結構查找到我們要插在哪個函數之后,接着我們去m_funcations結構中去查找相應的對象,獲取我們要插入的行號,最后進行插入操作即可,是不是有了頭文件的幫主,實現文件插入操作就變得相當簡單啦。
void QtCppDescription::GenerateFuncationCode(FuncType type, const QString & code, const QString & /*= ""*/)
{
QtHeaderDescription * headerDescrition = dynamic_cast<QtHeaderDescription *>(m_pDescription);
if (nullptr != headerDescrition)
{
const ClassDescription & classDesc = headerDescrition->GetDescription();
//獲取上一個函數名稱
int index = type;
const QMap<int, ScopePiece> & scopeIndexs = classDesc.pieceIndexs;
while (index >= 0)
{
if (scopeIndexs.contains(index) && scopeIndexs[index].funcations.size() != 0)
{
break;
}
--index;
}
if (index == -1)
{
return;
}
QString preFuncName = scopeIndexs[index].funcations.last().name;
int r = preFuncName.indexOf("(");
QString leftName = preFuncName.left(r).trimmed();
int l = leftName.lastIndexOf(" ");
QString preBaseName = leftName.mid(l + 1);
auto iter = std::find_if(m_funcations.begin(), m_funcations.end(), [preBaseName](const BaseItem & item) {
return item.name == preBaseName;
});
if (iter != m_funcations.end())
{
int insertRow = iter->end;
m_strRowContexts.insert(insertRow + 1, code);
m_bDirty = true;
AnalysisFile();
}
}
}
3、測試
3.1、測試代碼
QtGrammaAnalysis analysis;
QString oldFilePath = fileInfo.absoluteFilePath();
analysis.SetHeaderFile(oldFilePath);
analysis.SetCppFile(oldFilePath.replace(".h", ".cpp"));
analysis.GenerateDefinition("\nvoid test1()\n{\n\t\n}");
analysis.GenerateDeclaration("\tvoid test1();");
analysis.SetScopeType(FT_PROTECT_SLOT);
analysis.GenerateDefinition("\nvoid test1_1()\n{\n\t\n}");
analysis.GenerateDeclaration("\tvoid test1_1();");
analysis.SetScopeType(FT_PUBLIC_SLOT);
analysis.GenerateDefinition("\nvoid test1_2()\n{\n\t\n}");
analysis.GenerateDeclaration("\tvoid test1_2();");
analysis.Save();
3.2、實現文件測試結果
3.2、頭文件測試結果
四、源代碼
需要源代碼留郵箱,不提供csdn下載了,麻煩
簡書地址文章名稱
轉載聲明:本站文章無特別說明,皆為原創,版權所有,轉載請注明:朝十晚八 or Twowords