8

如何使用 libclang 获取原始文字的值?

例如,如果我有一个光标类型为 CXCursor_IntegerLiteral 的 CXCursor,我该如何提取文字值。


更新:

我在使用 libclang 时遇到了很多问题。我强烈建议完全避免它,而是使用 clang 提供的 C++ 接口。C++ 接口非常有用并且有很好的文档记录:http ://clang.llvm.org/doxygen/annotated.html

我现在看到的 libclang 的唯一目的是使用以下代码为您生成 ASTUnit 对象(否则并不容易):

ASTUnit * astUnit;
{
    index = clang_createIndex(0, 0);
    tu = clang_parseTranslationUnit(
        index, 0,
        clangArgs, nClangArgs,
        0, 0, CXTranslationUnit_None
        );
    astUnit = static_cast<ASTUnit *>(tu->TUData);
}

现在你可能会说 libclang 是稳定的,而 C++ 接口不是。这几乎无关紧要,因为你花时间用 libclang 找出 AST 并用它创建 kludges 无论如何都会浪费你很多时间。我会尽快花几个小时来修复版本升级后无法编译的代码(如果需要的话)。

4

4 回答 4

9

您无需重新解析原件,而是已经在翻译单元中获得了所需的所有信息:

if (kind == CXCursor_IntegerLiteral)
{
    CXSourceRange range = clang_getCursorExtent(cursor);
    CXToken *tokens = 0;
    unsigned int nTokens = 0;
    clang_tokenize(tu, range, &tokens, &nTokens);
    for (unsigned int i = 0; i < nTokens; i++)
    {
        CXString spelling = clang_getTokenSpelling(tu, tokens[i]);
        printf("token = %s\n", clang_getCString(spelling));
        clang_disposeString(spelling);
    }
    clang_disposeTokens(tu, tokens, nTokens);
}

您会看到第一个标记是整数本身,下一个是不相关的(例如,它;用于int i = 42;.

于 2012-06-19T19:58:06.363 回答
3

如果您有权访问 a CXCursor,则可以使用该clang_Cursor_Evaluate功能,例如:

CXChildVisitResult var_decl_visitor(
    CXCursor cursor, CXCursor parent, CXClientData data) {
  auto kind = clang_getCursorKind(cursor);

  switch (kind) {
  case CXCursor_IntegerLiteral: {
    auto res = clang_Cursor_Evaluate(cursor);
    auto value = clang_EvalResult_getAsInt(res);
    clang_EvalResult_dispose(res);

    std::cout << "IntegerLiteral " << value << std::endl;

    break;
  }
  default:
    break;
  }

  return CXChildVisit_Recurse;
}

输出:

IntegerLiteral 42
于 2020-09-12T11:47:54.410 回答
1

我通过参考原始文件找到了一种方法:

std::string getCursorText (CXCursor cur) {
    CXSourceRange range = clang_getCursorExtent(cur);
    CXSourceLocation begin = clang_getRangeStart(range);
    CXSourceLocation end = clang_getRangeEnd(range);
    CXFile cxFile;
    unsigned int beginOff;
    unsigned int endOff;
    clang_getExpansionLocation(begin, &cxFile, 0, 0, &beginOff);
    clang_getExpansionLocation(end, 0, 0, 0, &endOff);
    ClangString filename = clang_getFileName(cxFile);
    unsigned int textSize = endOff - beginOff;

    FILE * file = fopen(filename.c_str(), "r");
    if (file == 0) {
        exit(ExitCode::CANT_OPEN_FILE);
    }
    fseek(file, beginOff, SEEK_SET);
    char buff[4096];
    char * pBuff = buff;
    if (textSize + 1 > sizeof(buff)) {
        pBuff = new char[textSize + 1];
    }
    pBuff[textSize] = '\0';
    fread(pBuff, 1, textSize, file);
    std::string res(pBuff);
    if (pBuff != buff) {
        delete [] pBuff;
    }
    fclose(file);
    return res;
}
于 2012-05-21T21:09:30.203 回答
1

您实际上可以结合使用 libclang 和 C++ 接口。

libclangCXCursor类型包含一个data字段,该字段包含对底层 AST 节点的引用。我能够通过转换为类型成功地访问该IntegerLiteral值。data[1]IntegerLiteral

我在Nim中实现了这一点,因此我将提供 Nim 代码,但您也可以在 C++ 中执行相同的操作。

let literal = cast[clang.IntegerLiteral](cursor.data[1])
echo literal.getValue().getLimitedValue()

IntegerLiteral类型是这样包装的:

type
  APIntObj* {.importcpp: "llvm::APInt", header: "llvm/ADT/APInt.h".} = object
    # https://github.com/llvm-mirror/llvm/blob/master/include/llvm/ADT/APInt.h
  APInt* = ptr APIntObj

  IntegerLiteralObj* {.importcpp: "clang::IntegerLiteral", header: "clang/AST/Expr.h".} = object
  IntegerLiteral* = ptr IntegerLiteralObj


proc getValue*(i: IntegerLiteral): APIntObj {.importcpp: "#.getValue()".}
  # This is implemented by the superclass: https://clang.llvm.org/doxygen/classclang_1_1APIntStorage.html
proc getLimitedValue*(a: APInt | APIntObj): culonglong {.importcpp: "#.getLimitedValue()".}

希望这可以帮助某人:)

于 2017-12-31T13:02:36.013 回答