logo

CppFront

Posted on: 2023-01-29

Herb Stutters new syntax for C++

Conclusion

All in all pretty interesting. The declaration syntax has a lot in common with Odin.

Herb talks about how backward compatibility with C, via cfront was key to the adoption of C++. That transition wasn't fast - it took 10 years.

It seems like a variety of languages are tacking the parameter passing issues. Val seems to be more academically thought through than the "parameter styles" here, but it's worth thinking through more about what they mean and what the trade offs are.

The named return value mechanism, is nice. It might be better than Odins in so far as it allows multiple named return values. They are passed back in a struct.

Since it's based on C++ there are some potential performance issues - for example move requires the source to still be able to run a dtor. Some of the other examples seem to add extra overhead (like the uninitalized bool check on out).

Not having some kind of spec, or manual around the language, makes it hard to analyze what we have and why. Most of what I have here came from the CppCon 2022 talk.

Good

Not sure

Bad

Ugly

Discussion

From watching the CppCon talk

Is claimed to not be a new language per se, but an experiment for future.

Introduces a new syntax

name : () -> string = { 
    // do something
    return "some string"
}

name introduces a new name for a thing. The section afterwards is the "type", in this case it's a function that takes no parameters and returns a string. The equal introduces the implementation. If the implementation is just an expression it's not necessary to have the braces.

Has inout and the like.

For creating something on the heap can use

s := new<std::string>("world");

There are two "allocators" unique and shared. The default is unique, and that is what the statement above uses.

Has "Uniform Function Call Syntax" mechanism (UFCS)

Talks a fair amount how can mix with C++ - important for adoption (uses TypeScript as another example).

MITRE 2022 Most Dangerous Software Weaknesses

Use is and as for casting. Can get rid of most C++ cast styles. Importance around is and as is it allows generic code.

Unary operators are suffix. So

a : int = 10;
aPtr : _ = a&;

Hmm. Same presumably applies to !, -(?), +, ~ and '*' (dereference). On looking on other examples, it does appear to be the case.

Added next, can be used on all loop types.

Looks like type declarations are in the "read left to right" order. So I see in an example

call_my_framework: (msg: * const char) = 
    std::cout << msg;

This is also an example of a function with a single statement, so doesn't need {};

For a lambda, you can just leave off the name, which is nice and consistent.

Guarentees initialization before use. Safe to have uninitialized variables.

Definate first/definate last use. Initialization safety use tracking.

in, inout needed for initialization safety. The out thing is a bit odd in so far as it is optional if it's initialized, so presumably there is a bool somewhere indicating if it is initialized.

Nice. It has a syntax for named returned variables (like Odin).

f: () -> (i: int, s: std::string) = {
    i = 10;
    s = "Hello";
    return;
}

// Destructuring (I think from C++) aka "structured bindings"
auto [a, b] = f();

It has the following parameter styles

named_function : (i: int) = print(i); // named function

main: () -> int = {
    vec: std::vector = ( 1, 2, 3, 4);

    // Lambda
    std::ranges::for_each(vec, : (i : int) = print(i); ); 

    // Range-for body
    for vec do : (i: int) = print(i); 
}

On the last point with for described as being situation where a function is called with different parameters...

How to do capture. "unnamed function capture" with $.

main: () -> int = {
    s := "ish\n";

    // Range-for body
    // The `s$` captures s in the lambda
    for vec do : (i: int) = { std::cout << i << s$; } );
}

Hmm not sure about that one. Uses it will post conditions.

Wants to get rid of reference from C++, because it only needed to exist for parameter passing, and adds lots of complexity to the language. The parameter passing styles presumably replace that.

Questions

Where do you put attributes? For example calling convention?

CppFront doesn't appear to have pointers (except in unsafe code).

What is the inout and other "parameter styles" mechanism being discussed? There appears to be a talk about that.

The capture idea is interesting, but if I use the variable more than once, do I need to repeat used of $? Or is it just the first.

Need more information on the "left to right" decl syntax.

Probably need to look into more around perfect forwarding.

Links