Automated C++ Function Definition Insertion for Qt Signals

Building upon prior work that automatically inserts signal declarations into C++ header files, this article focuses on automating the insertion of corresponding function definitions into implementation (.cpp) files. Since .cpp files are generally less complex than headers—lacking class structures and access specifiers—their parsing and modification logic is comparatively straightforward.

Implementation Strategy

The approach mirrors the header-file technique: parse the entire .cpp file to identify all existing function definitions and record their start and end line numbers. When inserting a new function definition, the system determines the appropriate insertion point by locating the last function in the relevant scope (e.g., public slots, protected slots) as defined during header analysis. The new code is then inserted immediately after that function’s closing brace.

Critically, the target insertion position is derived from the header file’s parsed structure. By referencing the last function declared in a given scope within the header, the implementation file parser can find the matching definition (if it exists) and append the new definition right after it.

Code Structure

The updated architecture includes:

  • QtGrammaAnalysis: Public interface for triggering parsing and code generation.
  • QtDescription: Abstract base class providing shared utilities and cross-referencing between header and implementation analyzers.
  • QtHeaderDescription: Parses header files and stores class, scope, and function metadaat.
  • QtCppDescription: Parses and modifies .cpp files based on header-derived context.

QtCppDescription Implementation

The QtCppDescription class handles .cpp file analysis and insertion:

class QtCppDescription : public QtDescription
{
    Q_OBJECT

public:
    QtCppDescription(QObject* parent = nullptr);
    ~QtCppDescription();

    void GenerateFuncationCode(FuncType type, const QString& code, const QString& = "") override;

protected:
    void AnalysisFile() override;

private:
    void AnalysisFunc(int& row);
    QList<BaseItem> m_functions;
};

Function Parsing Logic

When encountering a pattern resembling ReturnType ClassName::functionName(...), the parser begins capturing lines until the number of opening and closing braces {} balance, indicating the end of the function body:

void QtCppDescription::AnalysisFunc(int& row)
{
    QString signature = GenerateString(m_currentRow, row);
    int parenPos = signature.indexOf('(');
    int scopePos = signature.lastIndexOf("::", parenPos);
    QString funcName = signature.mid(scopePos + 2, parenPos - scopePos - 2);
    ++row;

    while (row < m_lines.size()) {
        QString block = GenerateString(m_currentRow, row);
        if (block.count('{') == block.count('}')) {
            BaseItem item;
            item.start = m_currentRow;
            item.end = row;
            item.name = funcName;
            m_functions.append(item);
            break;
        }
        ++row;
    }
}

Inserting New Definitions

To insert a new function definition, the system queries the header analyzer for the last function in the target scope (e.g., public slots). It then searches the parsed .cpp functions for a match by name and inserts the new code right after the matched function’s end line:

void QtCppDescription::GenerateFuncationCode(FuncType type, const QString& code, const QString&)
{
    auto* header = dynamic_cast<QtHeaderDescription*>(m_relatedAnalyzer);
    if (!header) return;

    const auto& classInfo = header->GetDescription();
    const auto& scopes = classInfo.pieceIndexs;

    int searchIndex = static_cast<int>(type);
    while (searchIndex >= 0) {
        if (scopes.contains(searchIndex) && !scopes[searchIndex].functions.isEmpty())
            break;
        --searchIndex;
    }

    if (searchIndex == -1) return;

    QString lastDecl = scopes[searchIndex].functions.last().name;
    int paren = lastDecl.indexOf('(');
    QString baseName = lastDecl.left(paren).trimmed();
    int lastSpace = baseName.lastIndexOf(' ');
    QString targetName = baseName.mid(lastSpace + 1);

    auto it = std::find_if(m_functions.begin(), m_functions.end(),
        [&targetName](const BaseItem& item) { return item.name == targetName; });

    if (it != m_functions.end()) {
        m_lines.insert(it->end + 1, code);
        m_modified = true;
        AnalysisFile(); // Re-parse to update internal state
    }
}

Testing

A test sequence demonstrates inserting multiple slot defiintions:

QtGrammaAnalysis analyzer;
analyzer.SetHeaderFile("example.h");
analyzer.SetCppFile("example.cpp");

analyzer.GenerateDeclaration("\tvoid test1();");
analyzer.GenerateDefinition("\nvoid Example::test1()\n{\n}");

analyzer.SetScopeType(FT_PROTECT_SLOT);
analyzer.GenerateDeclaration("\tvoid test1_1();");
analyzer.GenerateDefinition("\nvoid Example::test1_1()\n{\n}");

analyzer.SetScopeType(FT_PUBLIC_SLOT);
analyzer.GenerateDeclaration("\tvoid test1_2();");
analyzer.GenerateDefinition("\nvoid Example::test1_2()\n{\n}");

analyzer.Save();

The resulting .cpp file correctly places each new function definition after the last existing function in its respective scope, maintaining clean and organized code structure.

Tags: C++ Qt code-generation parsing AST

Posted on Sun, 17 May 2026 17:24:30 +0000 by $SuperString