利用libclang提取C++中enum值与名的映射

之前的一篇文章中,有思考过如何将enum的值与名进行映射,其中一种方法是利用工具进行预处理生成。最近由于项目中有类似的需求,所以学习了libclang,写了一个小工具实现映射。

整体流程非常简单:

  1. 匹配enum声明的AST节点。
  2. 获取enum声明AST节点的类型(即枚举类名前面附上作用域)
  3. 获取enum声明所在文件名(用于过滤系统头文件中的枚举)
  4. 获取enum声明所在文件行号(可用于区分同一命名空间的不同匿名enum声明)
  5. 获取enum声明下具体枚举量的值和名

代码如下:

#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Type.h"
#include "llvm/Support/CommandLine.h"

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace llvm;

DeclarationMatcher EnumDeclMatcher = enumDecl().bind("enum");

class EnumPrinter : public MatchFinder::MatchCallback
{
public:
    virtual void run(const MatchFinder::MatchResult &Result)
    {
        if (const EnumDecl *node = Result.Nodes.getNodeAs<clang::EnumDecl>("enum"))
        {
            LangOptions LO;
            PrintingPolicy PrintPolicy(LO);
            PrintPolicy.AnonymousTagLocations = false;
            PrintPolicy.SuppressTagKeyword = true;

            std::string typeString = QualType::getAsString((QualType(node->getTypeForDecl(), 0)).split(), PrintPolicy);
            PresumedLoc pLoc = node->getASTContext().getSourceManager().getPresumedLoc(node->getLocation());
            printf("%s [%s:%d]\n", typeString.c_str(), pLoc.getFilename(), pLoc.getLine());

            for (auto iter = node->enumerator_begin(); iter != node->enumerator_end(); iter++)
            {
                printf("    %s %ld\n", iter->getNameAsString().c_str(), iter->getInitVal().getExtValue());
            }
            printf("\n");
        }
    }
};

static llvm::cl::OptionCategory MyToolCategory("enumname options");
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);

int main(int argc, const char **argv)
{
    CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
    ClangTool Tool(OptionsParser.getCompilations(),
                   OptionsParser.getSourcePathList());

    EnumPrinter Printer;
    MatchFinder Finder;
    Finder.addMatcher(EnumDeclMatcher, &Printer);

    return Tool.run(newFrontendActionFactory(&Finder).get());
}

测试输入如下:

enum ENUM0
{
    ENUM00,
    ENUM01,
    ENUM02
};

enum class ENUMC0
{
    ENUMC00,
    ENUMC01,
    ENUMC02
};

namespace
{
    enum ENUM1
    {
        ENUM10,
        ENUM11,
        ENUM12
    };
    
    enum class ENUMC1
    {
        ENUMC10,
        ENUMC11,
        ENUMC12
    };
}

namespace ns
{
    enum ENUM2
    {
        ENUM20,
        ENUM21,
        ENUM22
    };

    enum class ENUMC2
    {
        ENUMC20,
        ENUMC21,
        ENUMC22
    };
}

class FOO
{
    enum ENUM3
    {
        ENUM30,
        ENUM31,
        ENUM32
    };

    enum class ENUMC3
    {
        ENUMC30,
        ENUMC31,
        ENUMC32
    };
};

namespace 
{
    namespace 
    {
        enum
        {
            ENUM40,
            ENUM41,
            ENUM42
        };
    }
}

输出为:

ENUM0 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:1]
    ENUM00 0
    ENUM01 1
    ENUM02 2

ENUMC0 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:8]
    ENUMC00 0
    ENUMC01 1
    ENUMC02 2

(anonymous namespace)::ENUM1 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:17]
    ENUM10 0
    ENUM11 1
    ENUM12 2

(anonymous namespace)::ENUMC1 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:24]
    ENUMC10 0
    ENUMC11 1
    ENUMC12 2

ns::ENUM2 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:34]
    ENUM20 0
    ENUM21 1
    ENUM22 2

ns::ENUMC2 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:41]
    ENUMC20 0
    ENUMC21 1
    ENUMC22 2

FOO::ENUM3 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:51]
    ENUM30 0
    ENUM31 1
    ENUM32 2

FOO::ENUMC3 [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:58]
    ENUMC30 0
    ENUMC31 1
    ENUMC32 2

(anonymous namespace)::(anonymous namespace)::(anonymous) [/home/imred/Documents/workspace/enumname/build/../test/main.cpp:70]
    ENUM40 0
    ENUM41 1
    ENUM42 2

为了容易理解,提取出来的数据都直接打印在屏幕上了,实际应用时,可按照某种数据格式进行输出(xmljson等),或者拼接输出成std::map<int, std::string>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章