AlgebraMaster

Modern C++ 创造非凡 . 改变世界

导航

clang reflection

生成注释

假设有下面的源码:

struct Vec3 {
  float x, y, z;
};

struct Vec4 {
  float x, y, z, w;
};

生成这样的代码:

//[[CLASS INFO]] class:Vec3, is pod:true, is aggregate:true
struct Vec3 {
  float x, y, z;
};
//[[CLASS INFO]] use struct-binding method :
//const auto &[_m0,_m1,_m2] = Vec3;



//[[CLASS INFO]] class:Vec4, is pod:true, is aggregate:true
struct Vec4 {
  float x, y, z, w;
};
//[[CLASS INFO]] use struct-binding method :
//const auto &[_m0,_m1,_m2,_m3] = Vec4;

 

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PreprocessorOutputOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include <format>
#include <iostream>

using namespace clang;

class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> {
public:
    MyASTVisitor(Rewriter &w ) : writer{w}{}

    bool VisitCXXRecordDecl(CXXRecordDecl *declaration) {
        auto sourceBeginLoc = declaration->getSourceRange().getBegin();
        auto sourceEndLoc = declaration->getSourceRange().getEnd();  

        declaration->dump();
auto name
= declaration->getNameAsString(); bool isPod = declaration->isPOD(); auto isAggregate = declaration->isAggregate(); auto fieldCount = std::distance(declaration->field_begin(), declaration->field_end()); std::string fieldBinding{"const auto &["}; for(auto i=0;i<fieldCount;i++){ fieldBinding += "_m" + std::to_string(i); if(i!=fieldCount-1) fieldBinding += ","; } fieldBinding += "] = "; fieldBinding += name; auto classInfo = std::format("//[[CLASS INFO]] class:{}, is pod:{}, is aggregate:{}\n", name, isPod, isAggregate); auto howToBind = std::format("\n//[[CLASS INFO]] use struct-binding method : \n//{};\n\n", fieldBinding); writer.InsertText(sourceBeginLoc, classInfo, true, true); writer.InsertText(declaration->getEndLoc().getLocWithOffset(2), howToBind,true, true); // location使用sourceEndLoc变量也可以 return true; } private: Rewriter &writer; }; class MyASTConsumer : public ASTConsumer { public: MyASTConsumer(Rewriter &w) : writer{w}{} void HandleTranslationUnit(ASTContext &context) override { MyASTVisitor visitor{writer}; visitor.TraverseDecl(context.getTranslationUnitDecl()); } Rewriter &writer; }; class MyFrontendAction : public ASTFrontendAction { public: void EndSourceFileAction() override{ auto &sm = writer.getSourceMgr(); writer.getEditBuffer(sm.getMainFileID()).write(llvm::outs()); } std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &compiler, StringRef file) override { llvm::outs() << "create ast consumer for: " << file << "\n"; writer.setSourceMgr(compiler.getSourceManager(), compiler.getLangOpts()); return std::make_unique<MyASTConsumer>(writer); } private: Rewriter writer; }; int main(int argc, const char **argv) { auto code= R"( struct Vec3 { float x, y, z; }; struct Vec4 { float x, y, z, w; }; )"; clang::tooling::runToolOnCode(std::make_unique<MyFrontendAction>(), code); return 0; }

 

输出:

D:\Plugin_Dev\CPP\LLVM\modify_ast\cmake-build-debug\xxx.exe

create ast consumer for: input.cc
CXXRecordDecl 0x23cdd3e7c48 <input.cc:3:1, line:5:1> line:3:8 struct Vec3 definition
|-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
| |-DefaultConstructor exists trivial needs_implicit
| |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| |-MoveConstructor exists simple trivial needs_implicit
| |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| |-MoveAssignment exists simple trivial needs_implicit
| `-Destructor simple irrelevant trivial needs_implicit
|-CXXRecordDecl 0x23cdd3e7d68 <col:1, col:8> col:8 implicit struct Vec3
|-FieldDecl 0x23cdd3e7e10 <line:4:3, col:9> col:9 x 'float'
|-FieldDecl 0x23cdd3e7e80 <col:3, col:12> col:12 y 'float'
`-FieldDecl 0x23cdd3e7ef0 <col:3, col:15> col:15 z 'float'
CXXRecordDecl 0x23cdd3e7f80 <input.cc:7:1, line:9:1> line:7:8 struct Vec4 definition
|-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
| |-DefaultConstructor exists trivial needs_implicit
| |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| |-MoveConstructor exists simple trivial needs_implicit
| |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param 
| |-MoveAssignment exists simple trivial needs_implicit
| `-Destructor simple irrelevant trivial needs_implicit
|-CXXRecordDecl 0x23cdd3e8098 <col:1, col:8> col:8 implicit struct Vec4
|-FieldDecl 0x23cdd3e8140 <line:8:3, col:9> col:9 x 'float'
|-FieldDecl 0x23cdd3e81b0 <col:3, col:12> col:12 y 'float'
|-FieldDecl 0x23cdd3e8220 <col:3, col:15> col:15 z 'float'                                
`-FieldDecl 0x23cdd3e8290 <col:3, col:18> col:18 w 'float'


//[[CLASS INFO]] class:Vec3, is pod:true, is aggregate:true
struct Vec3 {
  float x, y, z;
};
//[[CLASS INFO]] use struct-binding method :
//const auto &[_m0,_m1,_m2] = Vec3;



//[[CLASS INFO]] class:Vec4, is pod:true, is aggregate:true
struct Vec4 {
  float x, y, z, w;
};
//[[CLASS INFO]] use struct-binding method :
//const auto &[_m0,_m1,_m2,_m3] = Vec4;



Process finished with exit code 0

 

这句话输出的结果:

writer.getEditBuffer(sm.getMainFileID()).write(llvm::outs());

 

第一种方法输出到std::string

auto &sm = writer.getSourceMgr();
auto &buffer = writer.getEditBuffer(sm.getMainFileID());
std::string s;
llvm::raw_string_ostream os(s);
buffer.write(os);
std::cout << s << std::endl;

第二种:

auto startLoc = sm.getLocForStartOfFile(sm.getMainFileID());
auto endLoc = sm.getLocForEndOfFile(sm.getMainFileID());
SourceRange const range(startLoc, endLoc);
std::string const output_code = writer.getRewrittenText(range);
std::cout << output_code << std::endl;

 

 还有一个是输出到文件,这里用的llvm输出流 c++ - Clang using LibTooling Rewriter to generate new file? - Stack Overflow

// For each source file provided to the tool, a new FrontendAction is created.
class MyFrontendAction : public ASTFrontendAction {
public:
  MyFrontendAction() {}
  void EndSourceFileAction() override {
    SourceManager &SM = TheRewriter.getSourceMgr();
    llvm::errs() << "** EndSourceFileAction for: "
                 << SM.getFileEntryForID(SM.getMainFileID())->getName() << "\n";

    // Now emit the rewritten buffer.
  //  TheRewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs()); --> this will output to screen as what you got.
    std::error_code error_code;
    llvm::raw_fd_ostream outFile("output.txt", error_code, llvm::sys::fs::F_None);
    TheRewriter.getEditBuffer(SM.getMainFileID()).write(outFile); // --> this will write the result to outFile
    outFile.close();
  }
//as normal, make sure it matches your ASTConsumer constructor 
  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                 StringRef file) override {

    llvm::errs() << "** Creating AST consumer for: " << file << "\n";
    TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
    return llvm::make_unique<MyASTConsumer>(TheRewriter,&CI.getASTContext());
  }

 

 反射

我们网易自研了一个软件。核心中的代码反射到GUI空间,【是通过函数式编程实现。类似HDK,这个是传统方案】。

V2我将会 实现了3种方式:

1. 函数式编程硬写,写5行可能就GUI就展示5个界面

2. json 字符串,直接反序列化,本质还是用函数式编程,然后就到1这个步骤。

3. 这个方法就是接下来的方法:

    定义一个宏,这个宏本质式空的,通过clang展开代码。然后生成新的CPP代码,再次link进去compile.

    qt moc.exe也是这个本质,生成一个新代码,需要link xxx.moc.cpp

    我把生成的代码直接通过原本函数式编程注进去

 

 

1. clang/gnu __attribute__ 抽取REFLECTED信息:

对于下面的类,注意下面的语句只有clang/gnu ast支持,msvc并不支持。但是在msvc编译器下用clang前端可以分析这样的代码。(token,name 模拟了HDK的参数)

#define GUD_REFLECTED(token,name) __attribute__((annotate("reflected"),  annotate(#token),  annotate(#name)))

struct Vec3 {
    GUD_REFLECTED(chan0, value0) float x;
    GUD_REFLECTED(chan0, value1) float y;
    GUD_REFLECTED(chan0, value2) float z;
};

struct Vec4 {
  float x, y, z, w;
};

Vec3反射了3个变量。

Vec4反射0个变量。

 

在编译这个时候,抽取这个宏,然后,做一些事,首先先得到这个AST:

 bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {

        std::cout << "for class:" << Declaration->getNameAsString() << "\n";
        for (const auto *field : Declaration->fields()) {
            std::string fieldName = field->getNameAsString();
            std::cout << "for field:" << fieldName << ":";
            for(auto *attr: field->attrs() ){
                if (not AnnotateAttr::classof(attr)) continue;
                const auto *annotate_att = static_cast<const AnnotateAttr *>(attr);
                std::string str = annotate_att->getAnnotation().str();
                std::cout << str<< " ";
            }
            std::cout << "\n";
        }
        std::cout << "\n"; // new line for every class
        return true;
    }

源码:

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PreprocessorOutputOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/AST/Comment.h"
#include <format>
#include <iostream>

using namespace clang;

class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> {
public:

    bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {

        std::cout << "for class:" << Declaration->getNameAsString() << "\n";
        for (const auto *field : Declaration->fields()) {
            std::string fieldName = field->getNameAsString();
            std::cout << "for field:" << fieldName << ":";
            for(auto *attr: field->attrs() ){
                if (not clang::AnnotateAttr::classof(attr)) continue;
                const auto *annotate_att = static_cast<const AnnotateAttr *>(attr);
                std::string str = annotate_att->getAnnotation().str();
                std::cout << str<< " ";
            }
            std::cout << "\n";
        }
        std::cout << "\n"; // new line for every class
        return true;
    }

};

class MyASTConsumer : public ASTConsumer {
public:
    void HandleTranslationUnit(ASTContext &context) override {
        MyASTVisitor visitor{};
        visitor.TraverseDecl(context.getTranslationUnitDecl());
    }
};

class MyFrontendAction : public ASTFrontendAction {
public:
    void EndSourceFileAction() override{
        auto &sm = writer.getSourceMgr();
        auto &buffer = writer.getEditBuffer(sm.getMainFileID());
        buffer.write(llvm::outs());
    }

    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &compiler,
                                                   StringRef file) override {
        llvm::outs() << "create ast consumer for: " << file << "\n";
        compiler.getLangOpts().CPlusPlus = true;
        compiler.getPreprocessorOpts().addMacroDef("__cplusplus");
        compiler.getPreprocessorOpts().addMacroDef("Vec3=struct Vec3");
        compiler.getLangOpts().CPlusPlus2b = true;
        writer.setSourceMgr(compiler.getSourceManager(), compiler.getLangOpts());
        return std::make_unique<MyASTConsumer>();
    }
private:
    Rewriter writer;
};

/*
#define GUD_REFLECTED(token,name) __attribute__((annotate("reflected"), annotate(#token),  annotate(#name)))
struct test{
    GUD_REFLECTED(xxx) float x;
};*/


int main(int argc, const char **argv) {
    auto code= R"(
#define GUD_REFLECTED(token,name) __attribute__((annotate("reflected"),  annotate(#token),  annotate(#name)))

struct Vec3 {
    GUD_REFLECTED(chan0, value0) float x;
    GUD_REFLECTED(chan0, value1) float y;
    GUD_REFLECTED(chan0, value2) float z;
    GUD_REFLECTED(chan0, value3) float w;
};

struct Vec4 {
  float x, y, z, w;
};

)";

    clang::tooling::runToolOnCode(std::make_unique<MyFrontendAction>(), code);
    return 0;
}
View Code

执行结果:

create ast consumer for: input.cc
for class:Vec3
for field:x:reflected chan0 value0
for field:y:reflected chan0 value1
for field:z:reflected chan0 value2


for class:Vec4
for field:x:
for field:y:
for field:z:
for field:w:

有了这个结果,本质可以做很多事情。比如要做成json还是xml 还是 做成特殊的结构。

 

2. 为了支持window平台,因为代码仅仅使用clang做parse.

所以只要客户端代码是:

#if _GUD_CLANG_REFLECTED
#define GUD_REFLECTED(...) __attribute__((annotate(#__VA_ARGS__)))
#else
#define GUD_REFLECTED(...)
#endif

struct Vec3 {
    GUD_REFLECTED(chan0, value0) float x;
    GUD_REFLECTED(chan0, value1) float y;
    GUD_REFLECTED(chan0, value2) float z;
};

struct Vec4 {
  float x, y, z, w;
};

 

然后在跑clang的时候:

int main(){
    std::vector<std::string> CommandLineArgs = {"-fsyntax-only", "-D_GUD_CLANG_REFLECTED"};
    clang::tooling::runToolOnCodeWithArgs(std::make_unique<MyFrontendAction>(), code, CommandLineArgs);
}

 

3. 对类如法炮制:

 bool VisitCXXRecordDecl(CXXRecordDecl *decl) {
        std::cout << "[[CLASS]]:" <<decl->getNameAsString() << " REFLECTED INFO:" << std::endl;
        for(auto attr :decl->attrs()){
            if (not clang::AnnotateAttr::classof(attr)) continue;
            const auto *annotate_att = static_cast<const AnnotateAttr *>(attr);
            std::string str = annotate_att->getAnnotation().str();
            std::cout << str<< " " ;
        }
        std::cout << "\n";
        return true;
    }

此时源码就会成为:

#if _GUD_CLANG_REFLECTED
#define GUD_REFLECTED_FIELD(...) __attribute__((annotate(#__VA_ARGS__)))
#define GUD_REFLECTED_CLASS(...) class __attribute__((annotate(#__VA_ARGS__)))
#define GUD_REFLECTED_STRUCT(...) struct __attribute__((annotate(#__VA_ARGS__)))
#else
#define GUD_REFLECTED_FIELD(...)
#define GUD_REFLECTED_CLASS(...)
#define GUD_REFLECTED_STRUCT(...)
#endif


GUD_REFLECTED_STRUCT(Vec3, simple_vector3) Vec3 {
    GUD_REFLECTED_FIELD(chan0,value0)
    float x;
    GUD_REFLECTED_FIELD(chan0,value1)
    float y;
    GUD_REFLECTED_FIELD(chan0,value2)
    float z;
};

GUD_REFLECTED_STRUCT(Vec4, simple_vector4) Vec4 {
  float x, y, z, w;
};

 

输出:

[[CLASS]]:Vec3 REFLECTED INFO:
Vec3, simple_vector3
[[CLASS]]:Vec4 REFLECTED INFO:
Vec4, simple_vector4

 

 

 

参考:

编译器 LLVM Clang原理与实战 制作自己的编译器 source-to-source 源代码转换 编译遍 compile pass 代码插桩_llvm emitobjaction_EwenWanW的博客-CSDN博客

posted on 2023-04-02 10:01  gearslogy  阅读(151)  评论(0编辑  收藏  举报