querentns
querentns

Reputation: 73

How to easily know which special member functions the compiler has chosen?

The naïve approach of overriding each special memb func, and plonking a printf or cout in the body of each (e.g. C++: Implicit Member Functions here on SO; Vandevoorde and Josuttis call this "tracers"), does not appeal:

Giving -E (or -save-temps) to g++ causes the latter to emit a .ii file for every source .cpp (or .cxx) file; in such a .ii, the source will be annotated with the exact steps taken by the cpp preprocessor. I want, ideally, something similar but with source being instead annotated as

Foo baz;
//// Line 55, choosing   Foo::Foo(); implicit; empty body

Foo qux(Bar::mkFoo(42));
//// Line 56, choosing   Foo::Foo(int)
//// Line 56, choosing   Foo::Foo(const Foo&)
//// Line 56, choosing   Foo::~Foo(); implicit

Foo qux2(std::move(Bar::mkFoo(4.2)));
//// Line 57, choosing   Foo::Foo(double)
//// Line 57, choosing   Foo::Foo(Foo&&)
//// Line 57, choosing   Foo::~Foo(); implicit

The ideal approach will show choices as made for the particular optimization options given to the compiler.

Anyone know of a way to achieve this desideratum with g++? I'm ready to start writing a g++ plugin, but figured to ask before reinventing this particular wheel.

If there's a way to do this with clang, that'd be good to know as well.

Many thanks in advance.

Upvotes: 4

Views: 278

Answers (1)

Dietmar Kühl
Dietmar Kühl

Reputation: 153830

I would guess you can build something like this on top of clang. clang provides several libraries providing access to the internal data structures used, including an AST. The AST should contain lots of information, though, and it may not be obvious what's of interest.

For example, using this input

struct foo {
    foo();
    explicit foo(int);
    foo(foo&&);
    foo(foo const&);
    ~foo();
};

struct bar {
    static auto mkfoo(int x) -> foo;
};

template <typename T>
auto move(T&& t) -> T&&;

int main() {
    foo f0;
    foo f1{bar::mkfoo(17)};
    foo f2{move(bar::mkfoo(17))};
}

using the built-in command to dump the AST, i.e., using the command-line

clang -cc1 -std=c++11 -ast-dump ast.cpp

yields the following output below. If you look closely you will see most of the operations you are after. You may need to ignore the detail you are not interested, though.

TranslationUnitDecl 0x7fea3b82ccc0 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x7fea3b82d200 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
|-TypedefDecl 0x7fea3b82d260 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
|-TypedefDecl 0x7fea3b82d620 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
|-CXXRecordDecl 0x7fea3b82d670 <ast.cpp:1:1, line:7:1> line:1:8 referenced struct foo definition
| |-CXXRecordDecl 0x7fea3b82d780 <col:1, col:8> col:8 implicit referenced struct foo
| |-CXXConstructorDecl 0x7fea3b82d8b0 <line:2:5, col:9> col:5 used foo 'void (void)'
| |-CXXConstructorDecl 0x7fea3b873260 <line:3:5, col:21> col:14 foo 'void (int)'
| | `-ParmVarDecl 0x7fea3b82d980 <col:18> col:21 'int'
| |-CXXConstructorDecl 0x7fea3b873420 <line:4:5, col:14> col:5 used foo 'void (struct foo &&)'
| | `-ParmVarDecl 0x7fea3b873360 <col:9, col:12> col:14 'struct foo &&'
| |-CXXConstructorDecl 0x7fea3b8735e0 <line:5:5, col:19> col:5 foo 'void (const struct foo &)'
| | `-ParmVarDecl 0x7fea3b873520 <col:9, col:18> col:19 'const struct foo &'
| `-CXXDestructorDecl 0x7fea3b8736f0 <line:6:5, col:10> col:5 used ~foo 'void (void) noexcept'
|-CXXRecordDecl 0x7fea3b8737e0 <line:9:1, line:11:1> line:9:8 struct bar definition
| |-CXXRecordDecl 0x7fea3b8738f0 <col:1, col:8> col:8 implicit struct bar
| `-CXXMethodDecl 0x7fea3b873af0 <line:10:5, col:33> col:17 used mkfoo 'auto (int) -> struct foo' static
|   `-ParmVarDecl 0x7fea3b873990 <col:23, col:27> col:27 x 'int'
|-FunctionTemplateDecl 0x7fea3b873e70 <line:13:1, line:14:22> col:6 move
| |-TemplateTypeParmDecl 0x7fea3b873ba0 <line:13:11, col:20> col:20 referenced typename T
| |-FunctionDecl 0x7fea3b873dd0 <line:14:1, col:22> col:6 move 'auto (T &&) -> T &&'
| | `-ParmVarDecl 0x7fea3b873cc0 <col:11, col:15> col:15 t 'T &&'
| `-FunctionDecl 0x7fea3b8757b0 <col:1, col:22> col:6 used move 'auto (struct foo &&) -> struct foo &&'
|   |-TemplateArgument type 'struct foo'
|   `-ParmVarDecl 0x7fea3b8756b0 <col:11, col:15> col:15 t 'struct foo &&'
`-FunctionDecl 0x7fea3b873f10 <line:16:1, line:20:1> line:16:5 main 'int (void)'
  `-CompoundStmt 0x7fea3b875a48 <col:12, line:20:1>
    |-DeclStmt 0x7fea3b8740e0 <line:17:5, col:11>
    | `-VarDecl 0x7fea3b874020 <col:5, col:9> col:9 f0 'struct foo' callinit
    |   `-CXXConstructExpr 0x7fea3b874078 <col:9> 'struct foo' 'void (void)'
    |-DeclStmt 0x7fea3b875398 <line:18:5, col:27>
    | `-VarDecl 0x7fea3b874110 <col:5, col:26> col:9 f1 'struct foo' listinit
    |   `-ExprWithCleanups 0x7fea3b875380 <col:9, col:26> 'struct foo'
    |     `-CXXConstructExpr 0x7fea3b875348 <col:9, col:26> 'struct foo' 'void (struct foo &&)' elidable
    |       `-MaterializeTemporaryExpr 0x7fea3b875330 <col:12, col:25> 'struct foo' xvalue
    |         `-CXXBindTemporaryExpr 0x7fea3b8752c8 <col:12, col:25> 'struct foo' (CXXTemporary 0x7fea3b8752c0)
    |           `-CallExpr 0x7fea3b875290 <col:12, col:25> 'struct foo'
    |             |-ImplicitCastExpr 0x7fea3b875278 <col:12, col:17> 'auto (*)(int) -> struct foo' <FunctionToPointerDecay>
    |             | `-DeclRefExpr 0x7fea3b8741b8 <col:12, col:17> 'auto (int) -> struct foo' lvalue CXXMethod 0x7fea3b873af0 'mkfoo' 'auto (int) -> struct foo'
    |             `-IntegerLiteral 0x7fea3b875200 <col:23> 'int' 17
    `-DeclStmt 0x7fea3b875a30 <line:19:5, col:33>
      `-VarDecl 0x7fea3b8753c0 <col:5, col:32> col:9 f2 'struct foo' listinit
        `-ExprWithCleanups 0x7fea3b875a18 <col:9, col:32> 'struct foo'
          `-CXXConstructExpr 0x7fea3b8759e0 <col:9, col:32> 'struct foo' 'void (struct foo &&)'
            `-CallExpr 0x7fea3b875950 <col:12, col:31> 'struct foo':'struct foo' xvalue
              |-ImplicitCastExpr 0x7fea3b875938 <col:12> 'auto (*)(struct foo &&) -> struct foo &&' <FunctionToPointerDecay>
              | `-DeclRefExpr 0x7fea3b8758b0 <col:12> 'auto (struct foo &&) -> struct foo &&' lvalue Function 0x7fea3b8757b0 'move' 'auto (struct foo &&) -> struct foo &&' (FunctionTemplate 0x7fea3b873e70 'move')
              `-MaterializeTemporaryExpr 0x7fea3b875980 <col:17, col:30> 'struct foo':'struct foo' xvalue
                `-CXXBindTemporaryExpr 0x7fea3b875558 <col:17, col:30> 'struct foo' (CXXTemporary 0x7fea3b875550)
                  `-CallExpr 0x7fea3b875518 <col:17, col:30> 'struct foo'
                    |-ImplicitCastExpr 0x7fea3b875500 <col:17, col:22> 'auto (*)(int) -> struct foo' <FunctionToPointerDecay>
                    | `-DeclRefExpr 0x7fea3b8754a8 <col:17, col:22> 'auto (int) -> struct foo' lvalue CXXMethod 0x7fea3b873af0 'mkfoo' 'auto (int) -> struct foo'
                    `-IntegerLiteral 0x7fea3b8754e0 <col:28> 'int' 17

Upvotes: 2

Related Questions