Logo

Blog


C++ Insights new transformations

It's been a while since I last wrote about C++ Insights. Sorry for that. No worries, I still actively maintain the project.

I'm happy to report that C++ Insights now has two new transformations. I initially planned one, and stumbled over the second transformation. So you can say you get two for one.

Let me start with the, well, less C++ transformation.

Cfront

Do you remember the first ever C++ compiler, Cfront? I've done many classes in the past two years teaching C programmers C++. At one point I realized that I created C++ Insights to show people instead of only telling in the air.

I had the tool, it only needed little adjustments. You think I would learn, this was how I started C++ Insights and it turned out more difficult then I expected. Well, it didn't stop me.

You now can use a Cfront-mode in C++ Insights. It might not be perfect but at least for basic examples it should work fine. I consulted various sources I could find in the web for get as close as possible to the original output. Should you happen to have examples from the past how Cfront code looked like, please send them to me.

During supporting more and more constructs I stumbled over things I initially didn't thought about. I started adding basic support to show function overloading. I breaks for templates, but should be helpful to see which overload is picked.

Next, I started transforming references into pointers. Once I had done that I faced another construct which you all know, temporary objects. What to do about them. Make them visible, of course.

Temporaries

This brings me to the second transformation, showing temporary objects. I imagine this having a great benefit. In Cfront mode this transformation is always enabled, but you can also run it in C++ mode.

This can help to spot places where reasoning about is often hard. Here is an example cppinsights.io/s/ed467eb7 .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct list
{
    char data[5];

    char* begin() { return data; }
    char* end() { return &data[5]; }
};

struct Keeper
{
    list data{};

    auto items() const { return data; }
};

Keeper get()
{
    return {};
}

int main()
{
    for(auto& item : get().items()) {
    }
}

Usually you find it with a std::vector instead of list. At first glance the code looks good, but it contains UB. the issue is get().items().

Here is the insight after transforming the code with C++ Insights with the "Show object lifetime" option enabled:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Keeper __temporary23_26 = get();
list __temporary23_34 = static_cast<const Keeper &&>(__temporary23_26).items();
list && __range1 = static_cast<list &&>(__temporary23_34);
/* __temporary23_34 // lifetime ends here */
/* __temporary23_26 // lifetime ends here */
char * __begin1 = __range1.begin();
char * __end1 = __range1.end();
for(; __begin1 != __end1; ++__begin1) {
    char & item = *__begin1;
    /* item // lifetime ends here */
}

In the transformed code you can see that the object __range1 points to is destroyed before begin is called. A classic case of a dangling object.

Some transformations do not work together with Cfront. For example, lambdas aren't translated to C. This comes from the implementation strategy for CodeGenerator, the heart of C++ Insights. There are a couple of derived classes to customize behavior, like creating a special this for a lambda capture of this. One other instance is CfrontCodeGenerator. Which means its one or the other.

Support the project

You can support the project by becoming a Github sponsor or, of course, contributing with code.

Andreas