Logo

Blog


Hello C++ Insights

It occurred to me that I never really introduced C++ Insights on my blog. So here we go.

Have you ever wondered how some source code parts look internally from a compilers perspective? How features like range-based for loops are implemented? Then, you should try C++ Insights (https://cppinsights.io).

C++ Insights shows you your source code with the eyes of a compiler. The screenshot shows the web-frontend of C++ Insights:

C++ Insights.

Like the famous Compiler Explorer, you type your source code in the left and get the output on the right.

Now, what does "with the eyes of a compiler" mean? C++ Insights tries to address a problem I often observed when teaching C++. The language is great and with the recent standards, it shifts more work from the developer to the compiler. This results in more and more implicit code, which I think is great from a maintenance point of view. On the other hand, this code is hard to teach as we developers are used to see what’s going on. Thanks to the recent updates to the language, starting with C++11, we have more new features to explore and to understand. For example, the implications of decltype(auto) and parentheses.

Matt Godbolt's Compiler Explorer shows us the resulting binary from a certain C++ snipped in assembler. This gives us a better understanding on what the compiler does with our code. But my primary language as a C++ developer is C++. I'm OK with assembler but not with all sorts of assembler and I'm far better with C++.

For teaching and understanding only C++ helps for C++, at least in my opinion. This is why C++ Insights uses the Clang AST to transform a certain C++ code fragment into another, enriched, C++ code fragment. This enriched fragment shows all sorts of things the compiler does for us with our code. For example:

  • Compiler provided special member functions
  • Implicit conversions
  • Template instantiations
  • Operator calls
  • Which overload gets selected

and of course plenty more. The about page page already lists some examples.

To get a first impression, have a look at this example:

1
2
3
4
5
6
7
8
class Foo
{
};

int main()
{
  Foo f;
}

We are looking at an empty class which we instantiate in main. How can it be? There is no constructor declared by us. Running this code in C++ Insights gives you the following output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Foo
{
// public: inline constexpr Foo() noexcept;
// public: inline constexpr Foo(const Foo &);
// public: inline constexpr Foo(Foo &&);
};

int main()
{
  Foo f = Foo();
}

It shows the special member functions the compiler implicitly generates for us. As a plus we can also see, that these functions are implicitly inline and constexpr. Let's slightly change this example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Foo
{
public:
   Foo() = default;
   Foo(Foo &&){}
};

int main()
{
  Foo f;
}

We now provide a default constructor, which we ask the compiler to generate the code for us. Also we provide a move constructor. It’s not a big secret, that as soon as you provide a move constructor, the compiler stops generating the copy constructor for us. Run this code in C++ Insights and you can see that it’s true. Comment the move constructor and you will see that you get back all the special member functions.

C++ Insights shows for some constructs the underlying implementation, like range-based for loops and lambdas. By the way, lambdas are the hottest feature judging from issue reports and emails.

The overall goal is to generate code, which compiles. This isn’t always easy but so far it works well. However, there are some sharp edges, for example auto transformations.

Consider this sample fragment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
auto Foo()
{
  struct Point
  {
    int x;
    int y;
  };

  return Point{1,2};
}

The transformation with C++ Insights will lead to this result:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Point Foo()
{
  struct Point
  {
    int x;
    int y;
    // inline ~Point() noexcept = default;   
  };

  return Point{1, 2};
}

Which is correct, but the struct Point is declared and known only within Foo. This code won’t compile. To generate 100% compiling code, it would be necessary to detect such a case and move the definition of Point outside of Foo. I hope you can live with that restriction for now. If not, pull requests are always welcome.

As a side-note, you can run the C++ Insights executable locally from the command line. It’s also possible to run the web-frontend locally, if desired.

Some things worth to know when using C++ Insights

The goals of the project are:

  • Improve teaching C++.
  • The transformed code should compile.
  • Do not alter the code, stick to the AST as close as possible.
  • Do it right.

This doesn't mean that I sometimes fail at one of these, but if so, I try to correct the mistake as soon as possible.

One thing C++ Insights doesn’t do, is to show your optimized code. Everything happens in the front-end without optimizations.

The program is a Clang tool. This implies, that it shows the source code with the eyes of Clang. GCC may do things a little different. Keep that in mind.

For things like integer promotion rules, it is good to know that the web-frontend runs on a 64-bit Linux system.

Expect more C++ Insights articles in the future about what C++ Insights can do and how it can help teach C++. There is probably more you like to know. Feel free to tell me what you’d like to read either on Twitter or via mail (andy at cppinsights.io).

Have fun with C++ Insights. You can support the project by becoming a Patreaon or of course with code contributions.

Andreas