c++ 如何使用libclang确定成员函数是const还是volatile?

eqzww0vc  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(168)

我有一个CXCursorCXCursor_CXXMethod类型的示例。我想知道函数是const还是volatile,例如:

  1. class Foo {
  2. public:
  3. void bar() const;
  4. void baz() volatile;
  5. void qux() const volatile;
  6. };

我在libclang的文档中找不到任何有用的东西。我尝试了clang_isConstQualifiedTypeclang_isVolatileQualifiedType,但它们似乎总是在C++成员函数类型上返回0

bbmckpt7

bbmckpt71#

我可以想到两种方法:

    • 使用libclang lexer**

this SO answer中出现的代码为我工作;它使用libclang标记器来拆分方法声明,然后记录方法括号之外的任何关键字。
它不访问代码的AST,而且据我所知,它根本不涉及解析器。如果你确定你研究的代码是正确的C++,我相信这种方法是安全的。

  • 缺点 *:这个解决方案似乎没有考虑预处理指令,所以必须首先处理代码(例如,通过cpp传递)。

示例代码(要解析的文件必须是程序的第一个参数,例如:./a.out bla.cpp):

  1. #include "clang-c/Index.h"
  2. #include <string>
  3. #include <set>
  4. #include <iostream>
  5. std::string GetClangString(CXString str)
  6. {
  7. const char* tmp = clang_getCString(str);
  8. if (tmp == NULL) {
  9. return "";
  10. } else {
  11. std::string translated = std::string(tmp);
  12. clang_disposeString(str);
  13. return translated;
  14. }
  15. }
  16. void GetMethodQualifiers(CXTranslationUnit translationUnit,
  17. std::set<std::string>& qualifiers,
  18. CXCursor cursor) {
  19. qualifiers.clear();
  20. CXSourceRange range = clang_getCursorExtent(cursor);
  21. CXToken* tokens;
  22. unsigned int numTokens;
  23. clang_tokenize(translationUnit, range, &tokens, &numTokens);
  24. bool insideBrackets = false;
  25. for (unsigned int i = 0; i < numTokens; i++) {
  26. std::string token = GetClangString(clang_getTokenSpelling(translationUnit, tokens[i]));
  27. if (token == "(") {
  28. insideBrackets = true;
  29. } else if (token == "{" || token == ";") {
  30. break;
  31. } else if (token == ")") {
  32. insideBrackets = false;
  33. } else if (clang_getTokenKind(tokens[i]) == CXToken_Keyword &&
  34. !insideBrackets) {
  35. qualifiers.insert(token);
  36. }
  37. }
  38. clang_disposeTokens(translationUnit, tokens, numTokens);
  39. }
  40. int main(int argc, char *argv[]) {
  41. CXIndex Index = clang_createIndex(0, 0);
  42. CXTranslationUnit TU = clang_parseTranslationUnit(Index, 0,
  43. argv, argc, 0, 0, CXTranslationUnit_None);
  44. // Set the file you're interested in, and the code location:
  45. CXFile file = clang_getFile(TU, argv[1]);
  46. int line = 5;
  47. int column = 6;
  48. CXSourceLocation location = clang_getLocation(TU, file, line, column);
  49. CXCursor cursor = clang_getCursor(TU, location);
  50. std::set<std::string> qualifiers;
  51. GetMethodQualifiers(TU, qualifiers, cursor);
  52. for (std::set<std::string>::const_iterator i = qualifiers.begin(); i != qualifiers.end(); ++i) {
  53. std::cout << *i << std::endl;
  54. }
  55. clang_disposeTranslationUnit(TU);
  56. clang_disposeIndex(Index);
  57. return 0;
  58. }
    • 使用libclang的统一符号分辨率(USR)**

这种方法包括使用解析器本身,并从AST中提取限定符信息。

  • 优势 *:似乎适用于带有预处理器指令的代码,至少对于简单的情况是这样。
  • 缺点 *:我的解决方案解析USR,它没有文档,将来可能会更改。尽管如此,编写一个单元测试来防止这种情况很容易。

看看$(CLANG_SRC)/tools/libclang/CIndexUSRs.cpp,它包含生成USR的代码,因此包含解析USR字符串所需的信息。具体来说,第523 - 529行(在从www.llvm.org下载的LLVM 3.1源代码中)是限定符部分。
在某处添加以下函数:

  1. void parseUsrString(const std::string& usrString, bool* isVolatile, bool* isConst, bool *isRestrict) {
  2. size_t bangLocation = usrString.find("#");
  3. if (bangLocation == std::string::npos || bangLocation == usrString.length() - 1) {
  4. *isVolatile = *isConst = *isRestrict = false;
  5. return;
  6. }
  7. bangLocation++;
  8. int x = usrString[bangLocation];
  9. *isConst = x & 0x1;
  10. *isVolatile = x & 0x4;
  11. *isRestrict = x & 0x2;
  12. }

而在main()中,

  1. CXString usr = clang_getCursorUSR(cursor);
  2. const char *usr_string = clang_getCString(usr);
  3. std::cout << usr_string << "\n";
  4. bool isVolatile, isConst, isRestrict;
  5. parseUsrString(usr_string, &isVolatile, &isConst, &isRestrict);
  6. printf("restrict, volatile, const: %d %d %d\n", isRestrict, isVolatile, isConst);
  7. clang_disposeString(usr);

Foo::qux()上运行

  1. #define BLA const
  2. class Foo {
  3. public:
  4. void bar() const;
  5. void baz() volatile;
  6. void qux() BLA volatile;
  7. };

产生预期的结果

  1. c:@C@Foo@F@qux#5
  2. restrict, volatile, const: 0 1 1
  • 警告 *:你可能已经注意到,libclang的源代码应该是isVolatile = x & 0x2,而不是0x4,所以你可能应该用0x2替换0x4。可能我的实现(OS X)已经取代了它们。
展开查看全部
ikfrs5lh

ikfrs5lh2#

可以使用clang_getCursorPrettyPrinted()检测纯虚/常量函数。这个函数提供了完整的方法/函数原型(virtual,const,=0等)。--你在源代码中看到的一切)。如果你可以得到一个光标所需的函数/方法,这是所有你需要的。前面的答案告诉你如何获得光标。
下面的代码是用C++写的,但你可以把它翻译成C,因为它使用了libclang。
示例如何检查const方法(getAsStdString定义如下):

  1. auto funcPrettyPrinted = getAsStdString(
  2. clang_getCursorPrettyPrinted(cursor, nullptr));
  3. if (std::string::npos != funcPrettyPrinted.find(") const"))
  4. {
  5. break;
  6. }

如何检查虚函数的示例:

  1. auto funcPrettyPrinted = getAsStdString(
  2. clang_getCursorPrettyPrinted(cursor, nullptr));
  3. if (std::string::npos != funcPrettyPrinted.find("virtual") &&
  4. std::string::npos != funcPrettyPrinted.find("= 0"))
  5. {
  6. break;
  7. }

下面是一个漂亮的打印输出示例:

  1. virtual void one() = 0

不太重要但很有用的helper函数:

  1. std::string getAsStdString(CXString str)
  2. {
  3. auto cstr = clang_getCString(str);
  4. if (nullptr == cstr)
  5. {
  6. return "";
  7. }
  8. std::string stdStr{cstr};
  9. clang_disposeString(str);
  10. return stdStr;
  11. }

当然,你可以使用正则表达式来确保搜索是100%正确的。如果你需要的话

展开查看全部

相关问题