How to parse C++ 'struct' definition from source code using clang

Note: Also see our upgraded version of this post, How to export C++ struct fields to JSON using clang and boost::json.

The clang library provides a relatively easy way to parse C/C++ code and extract information about struct definitions.

The following code demonstrates how to parse the following struct definition:

typedef struct {
    double a; /* That weird parameter */
    double b; /* Another weird parameter */
} myParameters;

Full source code

#include <clang-c/Index.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstring>

void extractStructFields(CXCursor cursor) {
    CXCursorKind kind = clang_getCursorKind(cursor);
    if (kind == CXCursor_StructDecl) {
        CXString structName = clang_getCursorSpelling(cursor);
        std::cout << "Struct: " << clang_getCString(structName) << std::endl;
        clang_disposeString(structName);
        
        clang_visitChildren(cursor, [](CXCursor c, CXCursor parent, CXClientData client_data) {
            CXCursorKind kind = clang_getCursorKind(c);
            if (kind == CXCursor_FieldDecl) {
                CXString fieldName = clang_getCursorSpelling(c);
                CXType fieldType = clang_getCursorType(c);
                CXString typeName = clang_getTypeSpelling(fieldType);
                std::cout << "  Field: " << clang_getCString(fieldName) << " (Type: " << clang_getCString(typeName) << ")" << std::endl;
                clang_disposeString(fieldName);
                clang_disposeString(typeName);
            }
            return CXChildVisit_Continue;
        }, nullptr);
    }
}

int main() {
    CXIndex index = clang_createIndex(0, 0);
    const char *code = R"(
        typedef struct {
            double a; /* That weird parameter */
            double b; /* Another weird parameter */
        } myParameters;
    )";

    CXUnsavedFile unsavedFile = {"test.cpp", code, (unsigned long)strlen(code)};
    CXTranslationUnit unit = clang_parseTranslationUnit(index, "test.cpp", nullptr, 0, &unsavedFile, 1, CXTranslationUnit_None);
    if (unit == nullptr) {
        std::cerr << "Failed to parse translation unit." << std::endl;
        return 1;
    }

    CXCursor cursor = clang_getTranslationUnitCursor(unit);
    clang_visitChildren(cursor, [](CXCursor c, CXCursor parent, CXClientData client_data) {
        extractStructFields(c);
        return CXChildVisit_Continue;
    }, nullptr);

    clang_disposeTranslationUnit(unit);
    clang_disposeIndex(index);
    return 0;
}

How to compile

On Ubuntu, install the required libraries using

sudo apt -y install libclang-19-dev

and compile the code using

g++ parse_struct.cpp -o parse_struct -std=c++17 -I/usr/lib/llvm-19/include -L/usr/lib/llvm-19/lib -lclang

Example output

Run using ./parse_struct

Struct: myParameters
  Field: a (Type: double)
  Field: b (Type: double)

Alternate build using cmake

You might need to adjust the versions (19.1.1) and other parameters here to get it working properly for your setup.

cmake_minimum_required(VERSION 3.10)
project(parse_structs)

# C++17 Standard festlegen
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# LLVM/Clang-Komponenten finden
find_package(LLVM 19.1.1 REQUIRED CONFIG)
find_package(Clang REQUIRED CONFIG)

# Include-Verzeichnisse hinzufügen
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CLANG_INCLUDE_DIRS})

# Executable erstellen
add_executable(parse_structs parse_structs.cpp)

# LLVM-Definitionen hinzufügen
target_compile_definitions(parse_structs PUBLIC ${LLVM_DEFINITIONS})

# Gegen libclang linken
target_link_libraries(parse_structs PUBLIC libclang)