一、概述
上一篇文章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("
void test1()
{
}");
analysis.GenerateDeclaration(" void test1();");
analysis.SetScopeType(FT_PROTECT_SLOT);
analysis.GenerateDefinition("
void test1_1()
{
}");
analysis.GenerateDeclaration(" void test1_1();");
analysis.SetScopeType(FT_PUBLIC_SLOT);
analysis.GenerateDefinition("
void test1_2()
{
}");
analysis.GenerateDeclaration(" void test1_2();");
analysis.Save();
3.2、实现文件测试结果
3.2、头文件测试结果
四、源代码
需要源代码留邮箱,不提供csdn下载了,麻烦
简书地址文章名称
转载声明:本站文章无特别说明,皆为原创,版权所有,转载请注明:朝十晚八 or Twowords