利用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>

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