之前的一篇文章中,有思考過如何將enum
的值與名進行映射,其中一種方法是利用工具進行預處理生成。最近由於項目中有類似的需求,所以學習了libclang
,寫了一個小工具實現映射。
整體流程非常簡單:
- 匹配
enum
聲明的AST節點。 - 獲取
enum
聲明AST節點的類型(即枚舉類名前面附上作用域) - 獲取
enum
聲明所在文件名(用於過濾系統頭文件中的枚舉) - 獲取
enum
聲明所在文件行號(可用於區分同一命名空間的不同匿名enum
聲明) - 獲取
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
爲了容易理解,提取出來的數據都直接打印在屏幕上了,實際應用時,可按照某種數據格式進行輸出(xml
、json
等),或者拼接輸出成std::map<int, std::string>
。