You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用libclang Python绑定获取类型的完全限定名(含模板类)

如何在Clang中获取类型的完全限定名(保留typedef/别名)

问题背景

我通过执行python clang_example.py解析头文件my_source.hpp中的函数和方法声明,现有代码只能获取非限定的类型名,无法得到从最外层作用域可直接引用的完全限定名,同时需要保留typedef和别名,不能解析到它们的底层类型。

原头文件 my_source.hpp

#pragma once 

namespace ns {

    struct Foo {
        struct Bar {};

        Bar fun1(void*);
    };

    using Baz = Foo::Bar;

    void fun2(Foo, Baz const&);
}

原解析脚本 clang_example.py

import clang.cindex
import typing


def filter_node_list_by_predicate(
    nodes: typing.Iterable[clang.cindex.Cursor], predicate: typing.Callable
) -> typing.Iterable[clang.cindex.Cursor]:

    for i in nodes:
        if predicate(i):
            yield i
        yield from filter_node_list_by_predicate(i.get_children(), predicate)


if __name__ == '__main__':
    index = clang.cindex.Index.create()
    translation_unit = index.parse('my_source.hpp', args=['-std=c++17'])

    for i in filter_node_list_by_predicate(
        translation_unit.cursor.get_children(), 
        lambda n: n.kind in [clang.cindex.CursorKind.FUNCTION_DECL, clang.cindex.CursorKind.CXX_METHOD]
    ):
        print(f"Function name: {i.spelling}")
        print(f"\treturn type: \t{i.type.get_result().spelling}")
        for arg in i.get_arguments():
            print(f"\targ: \t{arg.type.spelling}")

现有输出

Function name: fun1
        return type:    Bar
        arg:    void *
Function name: fun2
        return type:    void
        arg:    Foo
        arg:    const Baz &

期望输出

需要获取函数、返回类型和参数类型的完全限定名,同时保留typedef/别名,输出如下:

Function name: ns::Foo::fun1
        return type:    ns::Foo::Bar
        arg:    void *
Function name: ns::fun2
        return type:    void
        arg:    ns::Foo
        arg:    const ns::Baz &

尝试过的方法

我试过使用Type.get_canonical(),代码如下:

print(f"\treturn type: \t{i.type.get_result().get_canonical().spelling}")
for arg in i.get_arguments():
    print(f"\targ: \t{arg.type.get_canonical().spelling}")

但该方法会解析typedef和别名,比如fun2的第二个参数会被解析为const ns::Foo::Bar &,不符合保留const ns::Baz &的需求。

补充需求(模板场景)

实际应用中还需要正确解析模板类及嵌套类型,例如以下代码:

namespace ns {
   template <typename T>
   struct ATemplate {
      using value_type = T;
   };

   typedef ATemplate<Baz> ABaz;

   ABaz::value_type fun3();
}

期望返回类型解析为ns::ABaz::value_type,退而求其次可接受ns::ATemplate<Baz>::value_type,但不能是ns::ATemplate::value_type或ns::ATemplate<ns::Foo::Bar>::value_type。

若Python绑定功能不足,可迁移到C++ API。


解决方案

1. Python绑定实现思路

要获取保留typedef/别名的完全限定类型名,核心是通过类型对应的**游标(Cursor)**来获取其限定名,而非直接使用Type.spelling或get_canonical()。步骤如下:

  • 对于任意Type对象,先通过get_declaration()获取其对应的声明游标;
  • 若游标有效,获取游标的完全限定名(通过get_qualified_name()方法);
  • 处理类型的修饰符(如const、&、*等),将限定名与修饰符组合;
  • 特殊处理内置类型(如void),直接返回其拼写。

修改后的Python代码示例:

import clang.cindex
import typing


def filter_node_list_by_predicate(
    nodes: typing.Iterable[clang.cindex.Cursor], predicate: typing.Callable
) -> typing.Iterable[clang.cindex.Cursor]:
    for i in nodes:
        if predicate(i):
            yield i
        yield from filter_node_list_by_predicate(i.get_children(), predicate)


def get_qualified_type_name(typ: clang.cindex.Type) -> str:
    # 处理类型修饰符的辅助函数
    def get_type_modifiers(t: clang.cindex.Type) -> str:
        modifiers = []
        # 处理引用
        while t.kind == clang.cindex.TypeKind.LVALUEREFERENCE or t.kind == clang.cindex.TypeKind.RVALUEREFERENCE:
            modifiers.append("&" if t.kind == clang.cindex.TypeKind.LVALUEREFERENCE else "&&")
            t = t.get_pointee()
        # 处理指针
        while t.kind == clang.cindex.TypeKind.POINTER:
            modifiers.append("*")
            t = t.get_pointee()
        # 处理const/volatile
        if t.is_const_qualified():
            modifiers.insert(0, "const")
        if t.is_volatile_qualified():
            modifiers.insert(0, "volatile")
        return " ".join(modifiers)
    
    base_type = typ
    # 先剥离所有修饰符,获取基础类型
    while (base_type.kind in [clang.cindex.TypeKind.LVALUEREFERENCE, clang.cindex.TypeKind.RVALUEREFERENCE,
                             clang.cindex.TypeKind.POINTER]):
        base_type = base_type.get_pointee()
    
    # 获取基础类型的声明游标
    decl_cursor = base_type.get_declaration()
    modifiers = get_type_modifiers(typ)
    
    # 处理内置类型(无有效声明游标)
    if decl_cursor.kind == clang.cindex.CursorKind.NO_DECL_FOUND:
        type_name = base_type.spelling
    else:
        # 获取完全限定名
        type_name = decl_cursor.get_qualified_name()
    
    # 组合修饰符和类型名
    if modifiers:
        # 处理const &这种顺序
        if "const" in modifiers and ("&" in modifiers or "*" in modifiers):
            return f"{modifiers} {type_name}"
        return f"{type_name} {modifiers}"
    return type_name


def get_qualified_function_name(cursor: clang.cindex.Cursor) -> str:
    return cursor.get_qualified_name()


if __name__ == '__main__':
    index = clang.cindex.Index.create()
    translation_unit = index.parse('my_source.hpp', args=['-std=c++17'])

    for func_cursor in filter_node_list_by_predicate(
        translation_unit.cursor.get_children(), 
        lambda n: n.kind in [clang.cindex.CursorKind.FUNCTION_DECL, clang.cindex.CursorKind.CXX_METHOD]
    ):
        print(f"Function name: {get_qualified_function_name(func_cursor)}")
        ret_type = func_cursor.type.get_result()
        print(f"\treturn type: \t{get_qualified_type_name(ret_type)}")
        for arg in func_cursor.get_arguments():
            arg_type = arg.type
            print(f"\targ: \t{get_qualified_type_name(arg_type)}")

2. C++ API实现思路

若Python绑定的功能不足以处理复杂模板场景,可使用Clang C++ API,核心逻辑一致:

  • 通过Type::getAs<Decl>()获取类型对应的声明;
  • 使用PrintingPolicy控制输出格式,禁用SuppressScope和SuppressUnwrittenScope,同时设置相关选项以保留typedef/别名;
  • 使用Type::getAsString(const PrintingPolicy &)生成符合要求的类型名。

示例代码片段:

#include <clang/AST/ASTContext.h>
#include <clang/AST/Type.h>
#include <clang/AST/Decl.h>
#include <clang/AST/PrettyPrinter.h>

std::string getQualifiedTypeName(const clang::Type *type, clang::ASTContext &ctx) {
    clang::PrintingPolicy policy = ctx.getPrintingPolicy();
    // 配置打印策略:保留限定名,不解析typedef
    policy.SuppressScope = false;
    policy.SuppressUnwrittenScope = false;
    policy.TerseOutput = false;
    policy.IncludeTagDefinitions = false;
    // 对于模板,保留原typedef名称而非展开
    policy.SuppressTemplateArgsInCXXConstructors = false;
    
    return type->getAsString(policy);
}

// 获取函数的完全限定名
std::string getQualifiedFunctionName(clang::FunctionDecl *funcDecl) {
    return funcDecl->getQualifiedNameAsString();
}

3. 模板场景处理说明

对于补充的模板代码场景,上述方案可以保留ABaz的typedef名称:

  • Python代码中,ABaz::value_type的声明游标会指向ABaz内部的value_type别名,其完全限定名为ns::ABaz::value_type;
  • C++ API通过PrintingPolicy的配置,会直接输出ns::ABaz::value_type,符合需求。

内容的提问来源于stack exchange,提问作者joergbrech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 16:25:28