Handle C++ functions with clang API to insert code

2019-03-10 16:58发布

问题:

I need to preprocess some C++ files to automatically insert code for testing and profiling, and I need to do it with the clang API.

For now, I want to be able to do the following: If there is a function:

int SomeFn(int a, int b, int c) {
    doStuff();
}

I want to preprocess it, that it looks like this:

int SomeFn(int a, int b, int c) {
    cout << "SomeFn is invoked with the following arguments: a=" << a << ", b=" << b << ", c=" << c << endl;
    doStuff();
}

I've tried to extend ASTConsumer and use the methods HandleTopLevelDecl and HandleTopLevelSingleDecl and check if the passed Decls are FunctionDecls and downcast them, so I can get their names and body-locations. But I can't handle class-methods that way, only global functions.

I found a function in the ASTConsumer class HandleTagDeclDefinition(TagDecl* D). The documentation says:

This callback is invoked each time a TagDecl (e.g. struct, union, enum, class) is completed.

But it looks like this method isn't even invoked, when I test it.

So my question is, what would be the right way to do this task? And how are C++ classes are represented in the clang API? Maybe someone knows, where I can find examples because the clang API is poorly documented.


UPDATE:

I know there is also the concept of Visitors and there is a method VisitRecordDecl(RecordDecl *D). The documentation says that RecordDecl represents classes. So I extended RecursiveASTVisitor and implemented VisitRecordDecl(RecordDecl *D) but it seems, that this method isn't invoked neither. Instead VisitVarDecl is invoked, when a class definition is find, as if it is seen as a variable declaration. So I'm a bit confused. I hope, someone can help...


UPDATE2:

I tried to parse another file and this time clang found one RecordDecl. But the file defined one struct and two classes, so I think my clang code parses C instead of C++. Are there any settings to switch between C and C++ ?

回答1:

Are there any settings to switch between C and C++ ?

Finally I found out how to handle this:

I extended from ASTConsumer and RecursiveASTVisitor<MyConsumer> to traverse the AST and implemented VisitCXXRecordDecl(CXXRecordDecl* D). Then I had to set the LangOptions parameter for the preprocessor, so it parses C++.

langOpts.CPlusPlus = 1;

My fault was to think that it would parse C++ right away, but that was not the case, it parses C as default and so doesn't recognize classes.



回答2:

I'm wondering why you want to do this transform on the fly (if I understand you right). This is especially tricky with C++ methods defined in-class, since they're compiled as-if they're defined outside the class (i.e. all class members are usable, even those not declared yet).

Anyway, when you have a RecordDecl, you can access its members via field_begin.

Obviously, when you encounter nexted classes, you'll need to enumerate those as well. In fact, since you can define methods in classes in functions, you'll need to check for nested declarations almost everywhere.



回答3:

I agree that the documentation of CLang might be lacking in some area. This is, unfortunately, the way Open Source works: it will lacking until someone needs it, figures it out and decide to contribute back his findings.

For you specific problem I suggest you post on the Clang Dev mailing list (or begin by sifting through the archives). Questions about ASTConsumers or other transform analysis are quite frequent and usually promptly answered to.

And then, when you've learned what you were looking for, do think about upgrading the documentation ;)



标签: c++ clang