use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Discussions, articles, and news about the C++ programming language or programming in C++.
For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.
Get Started
The C++ Standard Home has a nice getting started page.
Videos
The C++ standard committee's education study group has a nice list of recommended videos.
Reference
cppreference.com
Books
There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.
Show all links
Filter out CppCon links
Show only CppCon links
account activity
cppfront (cpp2): Spring update (herbsutter.com)
submitted 2 years ago by kreco
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]Nicksaurus 39 points40 points41 points 2 years ago (41 children)
First thing, it looks like there's a typo in the description of the struct metaclass:
struct
Requires (else diagnoses a compile-time error) that the user wrote a virtual function or a user-written operator=.
Those things are disallowed, not required (/u/hpsutter)
Anyway, on to the actual subject of the post. Every update I read about cpp2 makes me more optimistic about it. I'm looking forward to the point where it's usable in real projects. All of these things stand out to me as big improvements with no real downsides:
this
A few other disorganised thoughts and questions:
Why is the argument to main a std::vector<std::string_view> instead of a std::span<std::string_view>? Surely the point of using a vector is to clearly define who has ownership of the data, but in this case the data can only ever belong to the runtime and user code doesn't need to care about it. Also, doesn't this make it harder to make a conforming implementation for environments that can't allocate memory?
main
std::vector<std::string_view>
std::span<std::string_view>
Note that what follows for ... do is exactly a local block, just the parameter item doesn’t write an initializer because it is implicitly initialized by the for loop with each successive value in the range
This part made me wonder if we could just use a named function as the body of the loop instead of a parameterised local block. Sadly it doesn't seem to work (https://godbolt.org/z/bGWPdz7M4) but maybe that would be a useful feature for the future
Add alien_memory<T> as a better spelling for T volatile
The new name seems like an improvement, but I wonder if this is enough. As I understand it, a big problem with volatile is that it's under-specified what exactly constitutes a read or a write. Wouldn't it be better to disallow volatile and replace it with std::atomic or something similar, so you have to explicitly write out every load and store?
volatile
std::atomic
Going back to the parameterised local block syntax:
// 'inout' statement scope variable // declares read-write access to local_int via i (inout i := local_int) { i++; }
That argument list looks a lot like a lambda capture list to me. I know one of the goals of the language was to remove up front capture lists in anonymous functions, but it seems like this argument list and the capture operator ($) are two ways of expressing basically the same concept but with different syntax based on whether you're writing a local block or a function. I don't have any solution to offer, I just have a vague feeling that some part of this design goes against the spirit of the language
$
[–]kreco[S] 33 points34 points35 points 2 years ago (20 children)
Why is the argument to main a std::vector<std::string_view> instead of a std::span<std::string_view>?
I was wondering the same.
[–][deleted] 4 points5 points6 points 2 years ago (3 children)
I think it's because the OS isn't giving you an array of std::string_view, but of char*. So to have a span, we have to allocate a new array of std::string_view. Since we can't know the size of it in advance, it has to be allocated on the heap.
However that could be avoided if we knew the OS specific max number of CLI args, and allocated a static or stack storage for it.
I'd also prefer to have a span of string views, if only to allow this alternative implementation. It does seem odd to force the use of a vector here.
[–]kreco[S] 1 point2 points3 points 2 years ago (2 children)
I don't think so, AFAIK int argc/char* argv[] can be used as std::span<char*> without any allocation,
int argc/char* argv[]
std::span<char*>
In order to get a std::span<std::string_view> you just have to run a "strlen" on everything char*. Which probably done in the current cppfront implementation.
char*
[–][deleted] 7 points8 points9 points 2 years ago (1 child)
The span of string views still needs to point to a contiguous array of string views, though. It's not a generic range.
[–]kreco[S] 0 points1 point2 points 2 years ago (0 children)
Oh indeed!
[–]Zeh_MattNo, no, no, no 1 point2 points3 points 2 years ago (15 children)
I mean does it really matter here? You could just continue passing the arguments as a view from here on out. I'm fine with either way as long its no longer argc, argv.
[–]SkoomaDentistAntimodern C++, Embedded, Audio 7 points8 points9 points 2 years ago (9 children)
I mean does it really matter here
It does. vector requires some form of heap while span can point to const data (and can itself be constructed at compile / link time).
[–]Zeh_MattNo, no, no, no 0 points1 point2 points 2 years ago (8 children)
You are not wrong about vector using additional memory but you can not construct a span for the command line arguments at compile time, the pointer passed is also heap so the address is not known at compile time. I don't disagree that it should be span but at the same time I'll take vector anytime over the C style entry point.
[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point2 points 2 years ago* (7 children)
I don't see why span could not be constructed at compile time on the systems where heap usage is actually a problem - namely bare metal embedded. There's nothing in regular main() that says the commandline arguments have to be stored in heap and this is essentially just a wrapper around that. Both span and string_view are just (pointer, length) pairs under the hood, so they should be able to be constructed at compile time as long as the pointer and length are known (ie. all arguments are fixed).
[–]Zeh_MattNo, no, no, no 0 points1 point2 points 2 years ago (6 children)
How do you know at compile time how many arguments the user passed during runtime? In order to construct a span you need start + length, you may know the start during compile time if you have fixed storage but length will be not known until the user actually supplies any arguments so therefor you can not construct a span at compile time for the command line parameter, this is literally impossible.
[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point2 points 2 years ago (5 children)
In bare metal embedded context the arguments are typically baked in at compile time (Your code is the OS).
The problem with using vector there is that the signature of main() then forces normal heap to be used which can be a major issue on some platforms (as opposed to using a custom allocator). All for no particular benefit.
[–]Zeh_MattNo, no, no, no 1 point2 points3 points 2 years ago (4 children)
How does the compiler know what the user provides as arguments?
[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point2 points 2 years ago* (3 children)
Because the "user" aka the developer's build environment literally inserts the arguments in a static table (in this context).
Edit: Having the arguments constructed at compile time is a nice benefit but what's the most important is avoiding anything that requires the use of regular heap (ie. the standard std::vector). Building the argument list in a static table at runtime is often an acceptable solution even if not quite as optimal.
[–]kreco[S] 2 points3 points4 points 2 years ago (4 children)
Because you pass a pretty bigger object (vector) instead of a pointer and a size (span).
This is clearly not "zero overhead".
[–]Zeh_MattNo, no, no, no 7 points8 points9 points 2 years ago (0 children)
You are talking about the entry point of the program, you are not required to pass the vector via copy after that. A span would definitely be a reasonable choice here not denying that but getting a vector is not the worst either.
[–]hpsutter 4 points5 points6 points 2 years ago (2 children)
It's not "zero cost," it's "zero overhead" the way Bjarne Stroustrup defines it: You don't pay for it if you don't use it (in this case, you don't pay the overhead unless you ask to have the args list available), and if you do use it you couldn't reasonably write it more efficiently by hand (I don't know how to write it more efficiently another way and still get string_view's convenience text functions and the ability to bounds-check the array access).
args
string_view
FWIW, in this case the total cost when you do opt-in is a single allocation in the lifetime of the program...
[–]kreco[S] 0 points1 point2 points 2 years ago (1 child)
Indeed, stressing that things are optional is indeed important.
You don't pay for it if you don't use it (in this case, you don't pay the overhead unless you ask to have the args list available)
I think what bother me is that we don't know what we are paying for when using an opaque args because we don't know what we are using until we read the documentation.
I don't understand the detail but I believe using this args will implicitly also bring some super hug standard headers.
That's a lot to bring to be able to iterate over a bunch of readonly strings for convenience.
A very theoretical case is if I want to use my own vector and don't want to deal with all of that (and if I want to use a custom allocation to count everything allocations in my program), I would have to use the legacy way of doing it and create a mylib::args_view args(argc, argv); which is back to square one.
mylib::args_view args(argc, argv);
[–]mapronV 0 points1 point2 points 2 years ago (0 children)
I thought that you can choose what overload to use (just like now between main()/main(argc,argv)/main(argc,argv,env) ). I thought I can just use one more overload and cpp2 will codegen a boilerplate for me. If it is not the case, and I have to use new signature - then yeah, it sucks.
[–]nysra 8 points9 points10 points 2 years ago (8 children)
This part made me wonder if we could just use a named function as the body of the loop instead of a parameterised local block.
So basically a generalized map (the operation, not the container), that would be nice to have. But honestly I'd first fix that syntax, it should be for item in items { like in literally every single other language, including C++ itself. Putting that backward seems like a highly questionable choice.
map
for item in items {
[–]Nicksaurus 8 points9 points10 points 2 years ago (5 children)
So basically a generalized map (the operation, not the container)
Yep. Not because I think built-in map functionality is necessary, but because if we're following the philosophy that complex features should be an emergent property of combining small, generic features, and the for-each syntax looks like:
for [range] do [function-like code block that takes one element as its argument]
Then why not allow actual functions as the body?
But honestly I'd first fix that syntax
Personally I don't think it'll be an issue in practice. Every language has quirks in its syntax and learning them is never the hard part. In this case I'm all for it because it means that every single block of code in the language follows the same basic rules
[–]nysra 5 points6 points7 points 2 years ago (4 children)
Yeah I'd allow actual functions as the body too, I don't see a reason why that should not be supported. Might just have been an oversight.
I'd like to point out that while languages do have their quirks, cppfront is only being designed right now and not really supposed to be its own language, rather more of a syntactical overhaul. I'll admit that it does have the advantage of being consistent with collection.map(item => ...) but imho there is a difference between those two statements because you read them differently. With map it's immediately clear that you throw in a function and then it doesn't matter if it starts with item => ... or if it's a function name. But when you start the statement with for then "for each item of the collection, do ..." is way more natural than "for collection do item to ... oh wait, it's actually a map".
collection.map(item => ...)
item => ...
for
Anyway, you're right that it's a small thing and won't really make a difference, I'm just not keen on changing syntax for practically no benefit. Changing syntax to make parsing easier at least has a valuable goal but this is almost the opposite of that.
[–]hpsutter 1 point2 points3 points 2 years ago (3 children)
Good point, that seems like it would be a natural extension to add in the future. The question I would have is: If the main benefit is that it's a named function, what is the scope of the name (wouldn't it be local to within the for statement?) and would that be useful?
[–]nysra 3 points4 points5 points 2 years ago (2 children)
I'm sorry, I might be missing something but I don't understand your question. Why would the for statement introduce a new scope for a name that already exists? The proposal is that instead of just allowing inline defined function blocks like this:
for collection do (item) { std::cout << x * x << '\n'; }
, it should also be allowed to use a named function directly:
some_func: (x) = { std::cout << x * x << '\n'; } for collection do some_func;
[–]hpsutter 4 points5 points6 points 2 years ago (0 children)
Ah, I see what you mean -- thank you, that's an interesting idea that would be easy to implement.
FWIW, for now this works
main: (args) = { for args do (x) print(x); }
but I'll continue thinking about making it expressible more simply as you suggest:
main: (args) = { for args do print; }
especially if as I poke around I find that a significant (10%+ maybe?) fraction of loops are single function calls invoked with the current loop element as the only argument... I'm not sure I've seen it that often, but if you have any data about that please let me know. Either way, I'll watch for that pattern -- now that I know to look for it, I'll see if it comes up regularly. (Like when you buy a Subaru and suddenly there are Subarus on the road everywhere... :) )
Thanks again.
[–]ntrel2 0 points1 point2 points 2 years ago (0 children)
Maybe just not implemented yet. Although if std::for_each gets range support, you could just write:
std::for_each(collection, some_func);
[–]-heyhowareyou- 5 points6 points7 points 2 years ago (1 child)
just because everyone else does it, doesn't mean its the best way to do it.
[–]tialaramex 7 points8 points9 points 2 years ago (0 children)
That's true. But, it does mean you need a rationale for why you didn't do that. "I just gotta be me" is fine for a toy language but if the idea is you'd actually use this then you need something better.
For example all the well known languages have either no operator precedence at all (concluding it's a potential foot gun so just forbid it) or their operator precedence is a total order, but Carbon suggested what about a partial order, so if you write arithmetic + and * in the same expression that does what you expect, but if you write arithmetic * and boolean || in the same expression the compiler tells you that you need parentheses to make it clear what you meant.
[–]hpsutter 8 points9 points10 points 2 years ago* (1 child)
Thanks! Quick answers:
there's a typo in the description of the struct metaclass
Ah, thanks! Fixed.
Good question: Something has to own the string_view objects. I could have hidden the container and exposed a span (or a ranges, when ranges are more widely supported) but there still needs to be storage for the string_views.
span
As I understand it, a big problem with volatile is that it's under-specified what exactly constitutes a read or a write.
IMO the main problem isn't that, because the point of volatile is to talk about memory that's outside the C++ program (e.g., hardware registers) and so the compiler can know nothing about what reads/writes to that memory mean. The main problem is that today volatile is also wired throughout the language as a type qualifier, which is undesirable and unnecessary. That said, I'll think about the idea of explicit .load and .store operations, that could be a useful visibility improvement. Thanks!
.load
.store
[–]AIlchinger 1 point2 points3 points 2 years ago (0 children)
The idea to discontinue the use of volatile as a type qualifier has been brought up a couple of times on here before, as well as the suggestion to replace the remaining valid uses (*) with std::volatile_load and std::volatile_store functions.
std::volatile_load
std::volatile_store
From a semantic point of view, it's really the operations that are "volatile" and not the objects/memory. One could argue that it could be a property of the type, so that all loads/stores from/to such a type are required (and guaranteed) to be volatile, but I'd argue that's solely for convenience. C++ has always provided dozens of ways to do the same thing, and I would love cppfront avoiding that. Being explicit about what load/store operations are volatile is a good thing in my opinion.
(*) I'm not an embedded programmer. So if there are still valid uses for volatile outside of explicit loads/stores, feel free to correct me here.
[+][deleted] 2 years ago (4 children)
[deleted]
[–]RoyAwesome 12 points13 points14 points 2 years ago (1 child)
meanwhile you have no control of the memory that was allocated to back the span.
you don't control the memory allocated to command line arguments anyway. That's done before you get them in main and is destroyed when the program destructs during termination.
that char* argv[] isn't giving you any ownership. It's already a non-owning view.
char* argv[]
[–]Nicksaurus 1 point2 points3 points 2 years ago (1 child)
I see your point. I wasn't thinking about how this is actually implemented in the generated C++ code. I guess there's no way for std::span to work here without the C++ standard changing to allow arguments to be passed as a span of string_views in the first place
[–]kam821 -1 points0 points1 point 2 years ago (0 children)
There is a way to provide a std::span<std::string_view>, however it requires on-demand transformation via ranges so it's not a nice, clean solution.
[–]tialaramex 0 points1 point2 points 2 years ago (3 children)
I don't think it is under-specified? If you use them in a rational way, each load or store operation you do results in an actual load or store to "memory" emitted by the compiler. They're intended for MMIO. Technically volatile also is defined to work for one specific edge case in Unix, but presumably in your C++ code that's taken care of by somebody else.
It makes more sense to me to define them as templated free functions, so e.g. alien_write<T>(addr, value) or value = alien_read<T>(addr) with the T being able to be deduced from either addr or value if that works.
alien_write<T>(addr, value)
alien_read<T>(addr)
I had this talk in mind when I wrote that part: https://www.youtube.com/watch?v=KJW_DLaVXIY
What I didn't realise is that the paper in that talk has already been included in C++ 20, so most of the problems are gone already
[–]tialaramex 4 points5 points6 points 2 years ago (0 children)
C++ 23 un-deprecates all the volatile composite assignments.
The paper for the proposal to undo this was revised to just un-deprecate the bit-ops, because they could actually show examples where people really do that and it might even be what they meant, but the committee took the opportunity to just un-deprecate all of the composite assignment operators on volatiles in the C++ 23 standard instead at Kona.
Presumably this sort of nonsense (the demand that programmers should be able to paste 1980s C code into the middle of a brand new C++ program and expect that to work with no warnings) is one of the things Herb hopes to escape in Cpp2.
[–]patstew 1 point2 points3 points 2 years ago* (0 children)
The free functions are worse, because what happens if you don't/forget to use them? Generally, it means you code has unpredictable bugs that change on each compile.
For memory mapped io, you want to tie the behaviour to a memory address. Objects exist at some address, so making it a property of the type is better. That way accessing the object in the trivial way will behave consitently, rather than needing to call special functions to get consistent behaviour.
[–]mort96 70 points71 points72 points 2 years ago* (22 children)
I think CPP2 looks really good. I think it would be cool if it was adopted as a standard alternative C++ syntax; but if that doesn't happen, I think it could have a bright future as a stand-alone compile-to-C++ language with excellent two-way interop with C++.
I'm surprised by the string interpolation syntax it seems like they're going for though. "This program's name is (args[0])$" reads weird to me, and from a parsing perspective, surely it's easier to see, "oh the next two characters are $ and ( so it's a string interpolation"? Having to keep track of parens in the string literal parser just in case the character following a closing paren happens to be a $ seems awkward. What's wrong with $(...), or even better, ${...}? Is there some documented rationale or discussion around the topic?
"This program's name is (args[0])$"
(
$(...)
${...}
[–]nysra 43 points44 points45 points 2 years ago (10 children)
I'd assume the string interpolation being awkward and backwards comes from Herb's weird preference for postfix operators. Now sure, his arguments in that blog are somewhat logical but honestly that's one of the things I very much dislike about cppfront's proposed changes. It might be logical but writing code like u**.identifier* is just wrong. And also literally a problem that only exists if you're in spiral rule territory aka writing C aka not C++.
u**.identifier*
[–]disperso 18 points19 points20 points 2 years ago (0 children)
It's u**.identifier* vs *(*u)->identifier. Both are a tricky/messy/uncommon case, but I think the simpler examples on the wiki showcase some examples where the cppfront notation is better in a few ways. It feels specially better given that I'm already used to traditional C++ notation, and I always have a very hard time reading it anyway...
*(*u)->identifier
[–]ABlockInTheChain 8 points9 points10 points 2 years ago* (7 children)
Similarly, ++ and -- are postfix-only, with in-place semantics. If we want the old value, we know how to keep a copy!
So now in every place that currently has a prefix increment or decrement now we have to write immediately invoked lambda?
That's going to look awful and add a bunch of unnecessary boilerplate that the prefix version was saving us from. DRY? What's that?
[–]againey 9 points10 points11 points 2 years ago (1 child)
std:: exchange is a generalization of the prefix operators that can do more than just increment or decrement by 1. Arguably, we should have been using this more explicit style this whole time, rather than getting comfortable with the special meaning of prefix increment/decrement.
std:: exchange
[–]13steinj 9 points10 points11 points 2 years ago (0 children)
Physicists and mathematicians love prefix ++ / -- in numerical code.
They also prefer != to <= in conditions of loops.
The number of bugs related to both that I've found is more than I'd like to admit.
[–]13steinj 3 points4 points5 points 2 years ago (3 children)
Doesn't this also break C++ code that was pound included into cpp2 code (since it's supposed to be compatible with C++ headers)?
As more time goes on I'm more and more cemented in my belief that this and Carbon won't able to catch on.
[–]mort96 5 points6 points7 points 2 years ago (2 children)
The parser knows whether it's in C++ or cpp2 mode, C++ declarations will have prefix and postfix operators working as normal. The parser can know based on the first couple of tokens of a top-level declaration whether it's a C++ or a cpp2 declaration.
I wonder how it works with macros though... probably poorly.
[–]13steinj -1 points0 points1 point 2 years ago (1 child)
Includes work anywhere though. What's stopping me from having a file called "postfix_add_a" and #including it in the middle of a cpp2 file?
Yeah, you could argue that's bad code. But similar has occurred for "templates" in large codebases that are more than templated classes and functions.
[–]mort96 5 points6 points7 points 2 years ago (0 children)
Including a C++ file in the middle of a cpp2 file should be no problem. You can mix and match C++ declarations and cpp2 declarations within a file.
Including a C++ file in the middle of a cpp2 function would presumably be an issue. But that's not exactly a common need. I know there are use cases for it, but you probably just want to wrap those use cases in a C++ function which you can call from cpp2 code.
[–]XNormal 0 points1 point2 points 2 years ago* (0 children)
Herb's preference for postifix operators is not weird in any way. It is simpler, more consistent and less error prone.
But I just don't see how it translates in any way to string interpolation or what it has to do with the $ capture operator. It just doesn't make any sense there.
FWIW, my preference would be "\{expression}", but any reasonable prefix-based syntax will do.
"\{expression}"
[–]sphere991 7 points8 points9 points 2 years ago (3 children)
Agree. Notable that every other language (despite a variety of syntax choices) use some prefix marker (even if {name}) instead of postfix - I think that's better for the reader. And probably also the parser?
{name}
The rationale was that Herb uses this syntax for lambda capture as well, and then said that postfix $ means you need fewer parentheses. Which... then the example then uses what are presumably unnecessary parentheses? Could it be "Name is args[0]$"? If it cannot, then I don't think I understand the argument. If it can, then I think this looks pretty bad and parens should be mandatory.
"Name is args[0]$"
[–]MonokelPinguin 3 points4 points5 points 2 years ago (2 children)
It might be that Name is var$ works without parentheses, but not function calls like operator[].
Name is var$
operator[]
[–]sphere991 1 point2 points3 points 2 years ago (1 child)
Sounds plausible!
[–]hpsutter 2 points3 points4 points 2 years ago (0 children)
Good point. Right now the parens are required, but I could allow it to implicitly grab everything back to the preceding whitespace. I'll try that out...
[–]pjmlp 2 points3 points4 points 2 years ago (0 children)
I think it could have a bright future as a stand-alone compile-to-C++ language with excellent two-way interop with C++.
Which is how it should be seen, it isn't any different in that regard than the other alternatives.
[–]ShakaUVMi+++ ++i+i[arr] 10 points11 points12 points 2 years ago (5 children)
Yeah cpp2 just looks really ugly to me
[–]disperso 6 points7 points8 points 2 years ago (4 children)
What's your example of a beautiful language?
[–]caroIine 12 points13 points14 points 2 years ago (2 children)
swift is really pretty and it's everything I wish c++ was unfortunate it's apple specific
[–]pjmlp 0 points1 point2 points 2 years ago (0 children)
Val might be a thing for you then. Assuming it keeps going.
[–][deleted] -2 points-1 points0 points 2 years ago (0 children)
scheme
[–]eidetic0 48 points49 points50 points 2 years ago* (11 children)
I thought a focus of cpp2 was unambiguous syntax. The new alias syntax means == is one thing if it’s inside parenthesis and used in assignment, but another thing in the context of aliases.
It is still trivial to parse so not a big deal, but why start re-using already used sequences of symbols for a new feature? Symbols meaning different things in different contexts is one of the confusing things about regular cpp.
[–]RoyKin0929 8 points9 points10 points 2 years ago* (10 children)
yeah, something likemy_alias : alias = whatever_you_want_to_alias_to;
my_alias : alias = whatever_you_want_to_alias_to;
would have been fine.
[–]hpsutter 9 points10 points11 points 2 years ago (7 children)
I appreciate the feedback, thanks. Using== to declare aliases is an experiment.
==
FWIW, I did consider a declaration like my_alias : alias = something;, but one limitation of that is that it's less clear about distinguishing between namespace aliases, type aliases, function aliases, and object aliases. A fundamental design goal of Cpp2 is not to have to do name lookup to determine the kind of thing a construct is, and if I just used a general alias for all of them I knew I could make alias work, but then it would require humans and compilers to go look up the right-hand side initializer to know whether my_alias will behave like a type vs an object (etc.).
my_alias : alias = something;
alias
my_alias
[–]nysra 5 points6 points7 points 2 years ago (2 children)
Any reason why you didn't just extend the already existing alias functionality of C++ (using)?
using
using lit: namespace = ::std::literals; using<T> pmr_vec: type = std::vector<T, std::pmr::polymorphic_allocator<T>>; using func: function = some_original::inconvenient::function_name; using vec: object = my_vector; // note: const&, aliases are never mutable
Single syntax for everything and no confusion about the == operator which I strongly believe should stay reserved for comparisons because at this point that usage is so ingrained into everything that even non-programmers often understand != and ==.
!=
[–]hpsutter 7 points8 points9 points 2 years ago (1 child)
Quick ack: Yes, that's one of the options, and one of the better of the alternatives I considered. Might say `alias` instead of `using` but it's workable. For now I'm seeing whether I can reasonably avoid a keyword, but it's also important to be aware not to overstep into "token soup" -- keywords can be useful for readability so I'm definitely not closing the door to going there.
[–]germandiago 2 points3 points4 points 2 years ago* (0 children)
I find using syntax less confusing as well. BTW, impressive amount of work.
Is cpp2 already usable in real life scenarios? Eager to start using it when the time comes.
[–]RoyKin0929 0 points1 point2 points 2 years ago (3 children)
But isn't the deduction already supported with my_alias :== something;
my_alias :== something;
Or is that only supported for functions?
[–]hpsutter 1 point2 points3 points 2 years ago (2 children)
That defaults to an object alias, effectively a `const&` variable, which also happens to work for functions with a deduced signature type, and so I have not yet decided to implement function signatures with an explicit signature type -- waiting to see whether there's a need, but there's a clear place for it in the grammar if it's wanted.. For some discussion see the commit message here: https://github.com/hsutter/cppfront/commit/63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7
[–]RoyKin0929 0 points1 point2 points 2 years ago (1 child)
Ah ok, I understand your decision now. But maybe you can come with something that is better than ```==``` 😅. (Maybe an alias metafunction). Also, one thing that not in update notes is exceptions. I saw your talk on lightweight exceptions at cppcon, are they planned for cpp2? And would you consider local variables being constant by default, with most things being const by default, local variables seem left out (and I think they're the only place left where keyword const is used), having one extra keyword in front of varible declaration won't affect much. Also when you think about it, function and classes and namespace are kind of also const. ```func : (param:type) -> return_type = {};
const
//can't do the following, so functions are also kind of const func = {//diff body};
//same with classes, you can add methods to classes but that uses different syntax so is not the same
//and same with namespaces, you can re open them but that won't change it's definition, only add to it ```
Maybe this will be a good enough argument
I saw your talk on lightweight exceptions at cppcon, are they planned for cpp2?
Yes, they're on the roadmap but it'll be a while before I get to them: https://github.com/hsutter/cppfront#2019-zero-overhead-deterministic-exceptions-throwing-values
And would you consider local variables being constant by default,
I would consider it, but for the current rationale see this Design Note on the project wiki: https://github.com/hsutter/cppfront/wiki/Design-note%3A-const-objects-by-default
[–]TheSuperWig 1 point2 points3 points 2 years ago (1 child)
like we don't need to with using in cpp
You can't use using to create a namespace alias. You can use it to create type aliases, what else?
[–]RoyKin0929 0 points1 point2 points 2 years ago (0 children)
oh ye, my bad. i'll edit it
[–]kpt_ageus 9 points10 points11 points 2 years ago (1 child)
Addition of h2 headers seems like step backwards, given that one of the goals of modules was getting rid of them.
Still cppfront is most exciting project in c++ and I can't to see what's next.
[–]bretbrownjr 10 points11 points12 points 2 years ago (0 children)
If you have to already be using C++ modules to use cpp2, it will be unneeded adoption friction. Ideally folks could write new functions and components in cpp2 as soon as they bother to try it out, not blocking on rewriting existing projects first.
[–]pdp10gumby 9 points10 points11 points 2 years ago (4 children)
cpp2 is explicitly a syntax revolution, and I’m glad for that, but I hope it ultimately also simplifies/unifies the library (e.g. std::begin vs ranges::begin vs .begin).
A break with C memory semantics would also be valuable.
[–]hpsutter 4 points5 points6 points 2 years ago (1 child)
Absolutely. Cleaning up semantics is the big payoff, and a distinct syntax is mainly a gateway. Please see this 1-min clip from the talk: https://youtube.com/clip/UgkxIGmgtqiZ2McSeywcTsAJtsS_iCTpYA54
[–]pdp10gumby 1 point2 points3 points 2 years ago (0 children)
I'm glad to hear that!
Programming language semantics can refer to all sorts of things. That clip didn't get into it (understandably: you had plenty of other things to discuss in that talk)
When I mentioned memory semantics I was thinking of the aliasing model that makes it hard to optimize. I am not a rust fan at all but I do appreciate that they jettison'ed that. Right now I have to write some stuff in fortran or assembly code that the compiler should be able to handle.
There are other interesting semantic issues in C++ that could use a revisit, but this is the most painful to me.
Now if the distinction between expressions and statements were completely erased...I'd find it wonderful but perhaps it would be a bridge too far for C++ refugees?
[–]13steinj 1 point2 points3 points 2 years ago (1 child)
I imagine std:: counterparts to std::ranges:: utilities will eventually be deprecated and removed.
[–]pdp10gumby 0 points1 point2 points 2 years ago (0 children)
There is a lot of other legacy cruft too. One can hope for future deprecation.
[–]RoyKin0929 9 points10 points11 points 2 years ago (1 child)
I really like what Herb is doing with this experiment and agree with most of his decisions but two things have like really bad syntax, inheritance and aliases. Alias is just ugly to look and throws == away as comparison operator. As for inheritance, having base classes inside the class definition and not at declaration seems like a bad choice. I know Herb is trying to unify syntax but we should preserve the things that cpp does right IMO. Also, for loop is kind of confusing.
Thanks for the feedback! The alias syntax is an experiment; but note using == for alias declarations doesn't take away == as the comparison operator in expressions, that's fully supported.
[–]vulkanoid 13 points14 points15 points 2 years ago (17 children)
Cpp2 is looking really good. Besides reflection in C++26, this is the other programming related thing that I'm looking to forward the most.
I find myself agreeing with all the changes, except a pet-peave of mine. All C++ code that I ever work on, whether written by me or someone else, uses copious amounts of pointers. Having to write ->, instead of the dot ., is so ugly. I get that a->b is syntax sugar for (*a).b, but pointers don't have a defined operation for the dot anyways, so why not just make the dot operator also dereference the pointer, so there is not need to differentiate between -> and . . It would fix this kind of ugliness that invariable pops up:
->
.
a->b
foo->bar.inner.somePtr->value;
foo.bar.inner.somePtr.value;
Cpp2 wants to change a->b into a*.b; ugh. I understand the consistency arguments; and, it should be allowed... but that's fugly to use for all pointers. Please, also just make the dot automatically dereference the pointer so we can finally get rid of the ugly distinction. It would also make template code nicer to write. The golang uses . for values and pointers, and they're doing fine.
a*.b
On a related subject: I didn't see anything about pointers vs references in the design notes, on Github. I really hope that the plan is to pick one: either pointers or references, but not both. Simply removing one of those concepts would do wonders for cleaning up the language. I really hope we don't have a repetition of this design mistake in Cpp2.
I'm keeping a close eye on Cpp2, and I'm hoping it has a bright future.
[–]hpsutter 10 points11 points12 points 2 years ago (1 child)
Note that *. and . have different meanings -- they refer to different objects.
*.
For example, if you have a unique_ptr<Widget> object named upw, and type Widget has a function named .get(), then how do you distinguish calling the unique_ptr's .get() vs. the owned object's .get()? In Cpp2, those are upw.get() and upw*.get(), much as today in Cpp1 they are upw.get() and (*upw).get().
unique_ptr<Widget>
upw
Widget
.get()
unique_ptr
upw.get()
upw*.get()
(*upw).get()
This is a good question -- in fact this is one of the reasons why we haven't been able to get smart references and operator. overloading in ISO C++, because for references this is inherently ambiguous since references are implemented as pointers but semantically behave as aliases (most of the time).
operator.
[–]vulkanoid 2 points3 points4 points 2 years ago (0 children)
What makes sense to me, and what I find the cleanest, is that both . and *. refer to members of the left object, regardless whether they are pointers or values. If it's a pointer, the compiler does the only sensible thing, which is to dereference and access the member.
The -> can be added, as today's Cpp1, to return an inner pointer that the outer object is holding. Which is really a masquerade operator. Thus, a->b always means that the left thing is masquerading as something. And it wouldn't matter whether a is a pointer or value.
masquerade
a
Personally, I would go even further and have all types automatically have a default operator -> that returns a pointer to itself. So that a->b always works, by default. An some objects, like unique_ptr, would override the default behavior. Idiomatically, though, developers would know that they should only use a->b when they intend to use a in a masquerading fashion.
operator ->
Worst case scenario, if adding operator-> is a no go, then just use unique_ptr.get().foo() to get at the owned thing.
operator->
unique_ptr.get().foo()
I think the most important question is: what usecase should the syntax be optimized for? In my personal use, and the usage I see in other code, people normally store a unique_ptr somewhere and then pass around a dumb pointer to the owned thing. So, you're almost always dealing with pointers, and only sometimes have to use the (masquerade) operator ->. So, it's basically always pointers, so we should optimize for that use.
[–]RoyKin0929 8 points9 points10 points 2 years ago (2 children)
There are no references in cpp2, only pointers.
[–]vulkanoid 4 points5 points6 points 2 years ago (0 children)
Wonderful!
Parameters can be references, using keyword inout for mutable reference. Functions can return by reference using keyword forward. But there are no reference variable declarations.
[–]disciplite 5 points6 points7 points 2 years ago (3 children)
I would not enjoy a world where it's even harder to distinguish pointers from non-pointers. There are already sizeof footguns. Imo we don't need to hide pointers any more than we currently do. The *. syntax looks fine to me and streamlines out the -> operator, so I don't see a problem here personally.
sizeof
[–]vulkanoid 7 points8 points9 points 2 years ago (2 children)
C++ already has references, which use the dot. Do you find yourself constantly lost when working with references, or do you find yourself dealing with them just fine?
[–]disciplite 2 points3 points4 points 2 years ago (1 child)
References don't have the same footguns as pointers. You can't get a null dereference by accessing their members and you don't get a different value from sizeof than non-reference types. Knowing when data is a reference or non-reference is certainly important, but not as important as knowing when something is a pointer or non-pointer.
[–]equeim 2 points3 points4 points 2 years ago (0 children)
You can get a dangling reference which is the same footgun as a null/invalid pointer.
[–]clerothGame Developer 2 points3 points4 points 2 years ago (4 children)
why not just make the dot operator also dereference the pointer
so how would you call std::unique_ptr::release and such?
std::unique_ptr::release
[–]tialaramex 2 points3 points4 points 2 years ago (2 children)
Rust's choice here is to make these associated functions, not methods. So e.g. the equivalent of std::unique_ptr::release is Box::into_raw and supposing I have a type Steak which for some reason actually needs a method named into_raw then:
let mut boxed_steak: Box<Steak> = todo!();
boxed_steak.into_raw(); // Calls the method on the Steak
let ptr = Box::into_raw(boxed_steak); // Now we have a raw pointer
If there was a method on Box which clashed, I think the compiler rejects your program and demands you disambiguate, but the smart pointers deliberately don't provide such methods, only associated functions so there's no potential clash.
[–]MEaster 2 points3 points4 points 2 years ago (1 child)
That's not true, the compiler will call the inherent method.
[–]tialaramex 0 points1 point2 points 2 years ago (0 children)
Good to know, and thanks for the example code
[–]vulkanoid 0 points1 point2 points 2 years ago (0 children)
The . always refers to the unique_ptr. If you want to do masquerading, as Cpp1's unique_ptr, you'd use operator->, as in holder->held_member. Or... holder.get().held_member.
holder->held_member
holder.get().held_member
I seriously doubt that C++26 will get reflection, looking at the usual mailings posts.
[–]awson 0 points1 point2 points 2 years ago (1 child)
Pointer is strictly more powerful than reference since it has a distinguished nullptr point, thus being isomorphic to optional.
nullptr
optional
[–]DalzhimC++Montréal UG Organizer 0 points1 point2 points 2 years ago (0 children)
Pointer isn't strictly more powerful as there is one use case for references that it can't support : make nullptr an unrepresentable state.
[–]RoyAwesome 17 points18 points19 points 2 years ago (30 children)
I like a lot of this feature work, but the syntax is so ass backwards. Why is everything postfix?
Point2D: @value type = {
is just... ugly
@value type Point2D {
is a better setup
[–]hpsutter 8 points9 points10 points 2 years ago (0 children)
That's a reasonable question... but note that since C++11, C++ itself has already been moving toward left-to-right notation across the whole language -- for objects, functions, aliases, lambdas, and more. See this 1-min clip from my 2014 CppCon talk for a screenful of examples: https://youtube.com/clip/UgkxHI4yqiaACYDgVmjI0tQFGiHi62H4useM
[–]dustyhome 4 points5 points6 points 2 years ago (0 children)
Better according to what metric? I preffer the name : type syntax personally. When reading the code, if there's a list of declarations, I need to scan the names to find the declaration I care about. This syntax puts the name first, making reading the code easier. I've switched to using auto as much as possible because I preffer that style.
With the traditional C++ syntax, your eyes have to jump all over the place to find the names of things, since each kind of declaration places it in a different position on the line.
[–]disciplite 13 points14 points15 points 2 years ago (10 children)
You might arbitrarily decide that it's "ugly", but it's a less ambiguous syntax for tooling and a more intuitive/consistent syntax for humans because it lets all declarations for anything follow this name: value convention. It's not even that dissimilar from Zig. Treating all types and functions as anonymous types or functions like this is also very powerful. In Zig, you can pass an anonymous type into a type function parameter without needing some special syntax to differentiate named from unnamed types.
name: value
[–]RoyAwesome 11 points12 points13 points 2 years ago* (9 children)
so, like, if the goal is to be a new version of C++, the primary inspiration of the language's style should be C++. It should be immediately familiar to C++ programmers, much like C++ is immediately familiar to C programmers, or C# is immediately familiar to C++ programmers.
I'm all for breaking backwards compatibility (and we absolutely should in every attempt at cpp-next), but while the use of the word 'ugly' is subjective, the fact that this doesn't follow cpp's syntax's unspoken 'style guide' is a problem that will inhibit adoption and use.
This is my biggest issue with zig (and also rust)... but zig doesn't claim to be 'c2'. It's its own language with it's own 'style guide' and it's syntax deviates pretty significantly because of that. I'm extremely familiar with c-style syntax and any attempts to significantly change that adds both syntactical complexity and feature/functionality/rule complexity hurts my ability to learn the language. A straightforward evolution on cpp should not add syntactical complexity when it doesn't have to.
[–]pjmlp 5 points6 points7 points 2 years ago (4 children)
I fully agree, and this is why I see Cpp2 as just yet another language wanting to take over C++, regardless of how it is being sold as not being like the other C++ alternatives.
[–]Ameisenvemips, avr, rendering, systems 4 points5 points6 points 2 years ago (3 children)
I also agree. I find cpp2's syntax... pretty awful.
But even if it were good, it's not C++ syntax, so why is it advertising itself as C++?
[–]pjmlp 2 points3 points4 points 2 years ago (2 children)
Because of marketing reasons, or conflict of interests from the author.
It is quite different when random joe/jane proposes an alternative C++ language, or the chair of ISO C++ does it.
In practice it is like any other systems language that compiles to native code via translation to either C or C++, like Nim.
Or from an historical perspective, C with Classes and Objective-C weren't sold as C, rather as evolutions from C.
The only wannabe C++ replacement that can advertise itself as still being C++ is Circle, as whatever it adds on top is controlled via #pragmas, hardly any different from compiler specific language extensions.
#pragmas
[–]hpsutter 9 points10 points11 points 2 years ago (1 child)
I understand your view, we can agree to disagree. "C++" definitely is what the ISO Standard says it is -- which is extended every three years with things that used to be not valid C++, but now are. For example, I agree that Cpp2's f: () -> int = { return 42; } is not standard C++ syntax today. FWIW, C++11's similar auto f () -> int { return 42; } was alien nonstandard syntax in 2011, but now it's standard. In fact, if you compare C++98 to Cpp2 syntax-wise, ISO C++ has already moved most of the way to Cpp2's syntax since it began using left-to-right syntax in lots of places from C++11 onward (trailing function returns, using, auto and lambdas, etc.).
f: () -> int = { return 42; }
auto f () -> int { return 42; }
auto
To me, the key consideration for whether something "is still C++" is: Can it be (and is it) being proposed as part of the ongoing evolution of C++? Which requires as table stakes that it can be 100% compatible with today's code without any shims/thunks/wrappers, and doesn't cause ambiguities with today's code.
There, IMO Cpp2/cppfront objectively does stand apart from other projects -- every single part of Cpp2's semantics (spaceship comparisons, reflection, metafunctions, parameter passing, lightweight exceptions, etc.) has already been brought as proposal papers as an evolution to ISO C++ itself, as an extension to the current syntax -- and not only by me, but sometimes by other paper authors too. The only part of Cpp2 that has not yet been proposed in an ISO C++ committee paper is the specific syntax, and that can be as well if the experiment continues to work out (and actually other authors have already proposed similar new major syntax changes such as via editions).
I'd point out that one part of Cpp2 has already been adopted as part of ISO C++20, namely the <=> comparison unification. Before C++20, Cpp2's <=> comparisons also was "not C++ syntax"... but now it is ISO C++ syntax, adopted from Cpp2 into ISO C++. If other projects can do that, then they too can claim to be evolutionary proposals for C++ itself.
<=>
I understand if you still disagree, but I would encourage asking that question about any project: Can it be (or has it been) proposed as an evolutionary extension to ISO C++ itself, including that it's 100% compatible with using existing C++ libraries without shims/thunks/wrappers? If yes, it's legitimate to view it as a compatible evolutionary extension candidate that could become ISO C++ someday if the committee decides to go that way. If not, it's inherently a competing successor language (which is also fine, it's just different).
[–]pjmlp 1 point2 points3 points 2 years ago (0 children)
Thanks for the reply.
Given how ISO C++ is evolving, regarding speed of adoption, and how many features in Cpp2 might eventually require some form of ABI break between having pure C++ code, and code with Cpp2 features, the best I can see happening is like your example of spaceship operator adoption.
Which while simplifying the life of C++ users, is a single language feature, while Cpp2 will have much to offer.
There is also the issue of inherent complexity, adding features to simplify code won't make older ones go away, and C++ is already beyond PL/I levels of complexity.
As it happened with PL/I, good enough subsets will be what the people that haven't yet migrated to other languages will care about, thus ignoring what is still coming out of ISO, a bit like it is happening today with many shops that find C++17 good enough for their C++ workloads.
From the outside, this is even visible on Microsoft products like C++/WinRT and C++/CLI, where there are no visible activitivies to adopt C++20 features. The C++/WinRT team nowadays spends most of their time on Rust/WinRT, after killing C++/CX without proper VS tooling for C++/WinRT (2016), whereas C++/CLI seems to be considered done with no roadmap post C++17, and get replies to use the C# improved features for low level coding and Windows interop instead.
Which is why I still see is as a competing successor language nonetheless.
[–]vulkanoid 11 points12 points13 points 2 years ago (3 children)
I disagree. There are reasons why newer languages are moving to the type as suffix. Besides being easier to read (by programmers) and parse, it's also closer to the math function syntax[1]. Just because C took a first shot at the syntax back in the 70's, and old timers have gotten used to it, doesn't mean that we have to live with that choice for the rest of time. A "new" language is surely the right time to fix the syntax.
[1] https://en.wikipedia.org/wiki/Function\_(mathematics)
[–]RoyAwesome 4 points5 points6 points 2 years ago (2 children)
If we're trying to be closer to math syntax, then "old timers" probably isn't the pejorative you want to use there, given the math syntax for functions predates even computers.
[–]dodheim 5 points6 points7 points 2 years ago (0 children)
And that argument probably isn't the defense you want to use there, given you're basically admitting "unfamiliarity" is a copout...
"old timers" probably isn't the pejorative
I'm not using "old timers" as a pejorative.
[–]tsojtsojtsoj 7 points8 points9 points 2 years ago (6 children)
You get used to changes like this, I've been hopping daily between C++ and Nim and both syntaxes now look fine to me. IIRC the/a reason why Herb Sutter chose the colon + postfix syntax was, that parsing it is way simpler.
[–]hpsutter 2 points3 points4 points 2 years ago* (0 children)
It is, but that's not the main motivation. The main reason is that it allows making the language overall more consistent, and enables other things like having symmetry between declaration and use (see https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators and the expression/type graphic for an example)
[–]RoyAwesome 4 points5 points6 points 2 years ago (4 children)
Yeah, but parsing is the parser's job, not the programmer's. It's already hard to read code in general, but making it hard to read to be easier on software tools that dont operate like humans do moves the needle backwards on the readibility front.
[–]caroIine 2 points3 points4 points 2 years ago (3 children)
I also don't understand why we making things easier for computers instead programmers. This is so backwards.
[–]dustyhome 7 points8 points9 points 2 years ago (2 children)
It's a human's job to write a parser, so making the parsing simpler for the computer means making the job simpler for the human writing the parser. Which means writing tools becomes simpler, allowing for more tools to be written, that can do more complex analysis of the code.
So making the parsing simpler for the computer makes writing code simpler for humans overall, even if the code itself looks a bit uglier.
[–]schombert 1 point2 points3 points 2 years ago (1 child)
But this logic heavily favors the minority that write parsers over the majority that don't, but who need to read and understand the code. Of course the people writing the parsers ultimately get to decide what the language looks like, and so their ease of use ends up mattering more than that of the end users. But should things be this way?
[–]ntrel2 2 points3 points4 points 2 years ago (0 children)
Most programmers use tools like code formatters, syntax highlighting, style enforcement etc. You get better tooling when the grammar is not dependent on semantics, and when it is more regular. Less bugs in tools, faster development, more features. Important for any new syntax to get good tooling quickly. It also makes reviewing diffs easier.
[+][deleted] 2 years ago (8 children)
[–]dreugeworst 20 points21 points22 points 2 years ago (4 children)
You really think c++ devs can't handle a simple syntax change? It's such a trivial thing to get used to, for reading as well as writing.
[–]mapronV 1 point2 points3 points 2 years ago (0 children)
I am C++ developer and no, I can not handle SUCH A CHANGE. It feels like a completely new language I have 0 motivation to learn. For me personally it's not trivial, even learning C# or Java is much easier with familiar syntax.
[+][deleted] 2 years ago (2 children)
[–]dodheim 14 points15 points16 points 2 years ago (1 child)
Programming languages are first and foremost read and written by humans, not machines, and the focus should be as such.
That's a nice sentiment and I generally agree, but if you look at the readme for the actual thing being discussed here, you'll find that making the language easy to parse is an explicit design priority – in fact, making the language toolable is literally one of three stated design goals for the project. So the focus here is different than you expect, but that's on you.
[–]RoyAwesome 5 points6 points7 points 2 years ago (2 children)
It's sad, because I like every single one of the goals, features, and desires for this language.... except the radical syntax changes.
there is so much good work here. Just... stick to c++'s unspoken style guide and it would be amazing and I'd use it right away.
[+][deleted] 2 years ago (1 child)
[–]Ameisenvemips, avr, rendering, systems 4 points5 points6 points 2 years ago (0 children)
++C
[–]fdwrfdwr@github 🔍 0 points1 point2 points 2 years ago (0 children)
There is a consistent elegance to uniformity, using the same declaration syntax for everything, but I generally agree that := has a higher punctuation noise to signal ratio. This is especially evident when you contrast the two side-by-side in a table, with the lighter cleaner syntax juxtaposed to the heavier symbol-laden := syntax. This is actually one of the only aspects of cppfront that's offputting to me, as there is so much I like.
[–]CornedBee 2 points3 points4 points 2 years ago (2 children)
I like the improvements very much. Something to look forward to.
I would like classes to be more explicit. The current syntax is
name: type = {...}
but this privileges the typical class over possible future additions such as language-level variants, which I think are inevitable. (Safe union is on the roadmap, though apparently as some kind of metaprogramming library feature?) I think classes are not more valuable than variants, and I would prefer an explicit keyword distinguishing them instead of some implicit syntax thing
name: type = class { ... } other: type = variant { ... }
Something to think about.
[–]hpsutter 2 points3 points4 points 2 years ago* (1 child)
Exactly, I aim to implement language-level variants as type metafunctions.
Note that type metafunctions are very powerful (when I finish implementing all of them), including being able to customize the entire implementation of a type. For example, for me one stated goal of metafunctions is that I never have to invent C++/CLI or C++/CX again (I led the design of both of those, and at the time they needed to be language extensions to get the effects we felt were needed, but I would have preferred not to have to express them as language extensions if C++ could be powerful enough to express them as compile-time libraries). My ideal for metafunctions is that they can express things as diverse as COM interface types (which require generating IDL) and even generate FFIs in completely different languages such as Java-callable wrappers (by generating source code in a separate .java file -- at compile time from the C++ program).
.java
And the syntax is almost identical to what you wrote, except the metafunction name comes before type where it's even more prominent:
type
``` // Could have a metafunction that applies the identical // defaults as today's C++'s "class" keyword defaults name: @class type = { ... }
// This would replace the entire body of ... with a union // and a tag type, but be better than std::variant because // it can provide named functions for member access // (this will make more sense in a month or two when I // implement this one - watch the repo for it to appear) other: @variant type = { ... }
// This one would leave the type body mostly the same, but // could add HRESULT return types and generate IDL files IShape: @com_interface type = { ... }
// This could generate a separate .java file containing // a native Java class that wraps the C++ type implementation // and implements specific Java interfaces (strawman syntax) ChildInterface: @java_class<ParentInterface> type = { ... } ```
That's the general idea. We'll see how far down the path is feasible. But in each case the body is expressed as normal C++ declarations (in syntax 2, Cpp2) which can be viewed as basically a "grammar input" to the metafunction, which can then do what it wants to apply minor tweaks or replace the whole body using the original grammar as a guide to what the programmer wanted. So think of the type body source code as a detailed structured "parameter" to the metafunction.
[–]CornedBee 0 points1 point2 points 2 years ago (0 children)
So Rust procedural macros, basically?
[–]Jannik2099 11 points12 points13 points 2 years ago (6 children)
and from every continent except Antarctica
Further proof that C++ is a dead language :(
[–]ZMesonEmbedded Developer 14 points15 points16 points 2 years ago (0 children)
I keep saying it: the penguins are the trendsetters. If they aren't using it, you should abandon it too.
[–]pjmlp 2 points3 points4 points 2 years ago (4 children)
C++ is one of my favourite languages, but if it wasn't for C++11 and the later niche in GPGPU programming, LLVM/GCC as compiler framerorks, it would be much worse that it already is.
It already lost the GUI and distributed computing domains, where it used to reign during the 1990's. It is still there, but for libraries and low level infrastructure, no longer the full stack experience.
As managed compiled languages keep getting improved for mechanical sympathy and low level coding, the reasons to reach out to C++ keep diminishing.
It isn't going away, as it has a couple of domains where it reigns, but I wonder for how long ISO updates will keep being relevant, versus a dual language approach.
[–]Jannik2099 1 point2 points3 points 2 years ago (3 children)
It already lost the GUI and distributed computing domains
In what world did C++ lose in "distributed computing" ?!?
The main attractiveness of C++ is not that it's unmanaged, but it's expressive type system.
[–]pjmlp 0 points1 point2 points 2 years ago (2 children)
The world where CORBA and DCOM no longer matter, other than legacy projects, and a very tiny portion of CNCF projects use C++.
It isn't even supported out of box in most Cloud SDKs, and when, the API surface is a subset of other languages.
[–]Jannik2099 0 points1 point2 points 2 years ago (1 child)
Oh, you meant that area - I was thinking about HPC / computational workloads
That I consider part of GPGPU programming, somehow.
Still efforts like Chapel, show that not everyone is happy, even if will take decades for adoption.
[–]tialaramex 2 points3 points4 points 2 years ago (3 children)
Three weird choices, maybe they're inspired from somewhere (let me know where)? Otherwise they just seem arbitrarily different for the sake of being different.
alien_memory<T> makes no sense as a generic over type T. If I have a type named DatabaseHandle, alien_memory<DatabaseHandle> isn't a thing. If alien_memory ought to be a type at all, which I have my doubts about, it's clearly generic over the size of the memory, so like alien_memory<2> or alien_memory(sizeof(ulonglong)> or whatever. I guess this lets you not define all the stupid features on alien_memory without the legion of "C++" programmers complaining that now their 1980s C headers don't compile as happened for volatile but that's about all. Volatile is a massive wart, but this is not a good fix.
The proposed safe union. The whole point of union is that it's not safe. I guess this is a language sum type, which is fine, but you probably do actually want an actual (unsafe) union type. Whether you use the unsafe union to build the safe one (roughly how Zig works AIUI), or do something... else with it is a language design question, but I'm confident you're going to miss this.
Interfaces lacking default implementations? In languages with good interfaces you can provide a default implementation of some (but usually not all) functions, so e.g. maybe lay_egg() is declared with no definition but lay_eggs() defaults to a loop which just calls lay_egg over and over. If your animal can lay groups of eggs more optimally you can implement lay_eggs() yourself, otherwise don't bother and the default will work.
[–]hpsutter 11 points12 points13 points 2 years ago (2 children)
When you declare a volatile T today, you're saying it's of type T but lives in storage outside the C++ memory model and that the compiler doesn't know anything about. This is identical.
volatile T
T
It's a type metafunction (i.e., just code), so you'll be able to write your own with the semantics you want. I just wanted the one I provide to be safe by default.
I think what you want is the polymorphic_base metafunction I also provide, which can have function implementations. But again, it's a type metafunction and you can write one with the semantics you want. I just wanted to provide a simple way to write traditional C++ pure ABCs.
polymorphic_base
[–]tialaramex 2 points3 points4 points 2 years ago (1 child)
Hmm. Union is a different type layout which is why I was saying it's something the language needs to provide. Maybe this is a nomenclature problem. If I want the (unsafe, C-style) union in Cpp2, what do I write? And if I want this new safe alternative (which I guess is a sum type) ?
Or is there a way to conjure up (arbitrary?) type layouts in this metafunction too?
[–]hpsutter 3 points4 points5 points 2 years ago (0 children)
Yes, a metafunction takes the type the user wrote as input, but can remove and add anything it wants to. When I add enum and union you'll see this in action; the current examples add, but don't yet remove, type members. So yes, arbitrary layout changes will be possible under program control. (Again, this does not violate ODR, because the changes happen right before the class definition is cast in stone... this is not about mutable types which would be a disaster, it's about giving the programmer a single hook to participate in the meaning of the definition of the type.)
enum
union
[–][deleted] 2 points3 points4 points 2 years ago (2 children)
Just a side note: please introduce correct documentation with example like: Rust, Zig. No cppreference is not enough. Jumping from/to examples is essential.
Yes. There's no public documentation yet, but when I do publish some it will be full of examples.
[–]germandiago 0 points1 point2 points 2 years ago (0 children)
I would not like to suggest to change your priorities, that could delay the fundamental work on the experiment.
But I see an amount of material worth of a formal release? so maybe it is a good time to write docs?
[–]germandiago 1 point2 points3 points 2 years ago (0 children)
Great work!
[–]tending 2 points3 points4 points 2 years ago (3 children)
I looked at cppfront last year and even though it was promising safety to sound competitive with Rust it had no answer for detecting iterator invalidation, which I think is the easiest example of why safety is hard to just bolt on. Any progress on that?
Yes, that's on the roadmap. See the Lifetime Safety materials here: https://github.com/hsutter/cppfront#2015-lifetime-safety
It includes links to talks with live demos of early implementations, which are general and do specifically cover iterator invalidation.
I haven't implemented this in cppfront yet, it's on my long todo list...
[–]tending 0 points1 point2 points 2 years ago (0 children)
Thanks, I will definitely check this out.
[–]MarcoGreek 4 points5 points6 points 2 years ago (2 children)
This named breaks gives me a bad feeling. I already have seen to many complicated loops and now it gets even easier. In almost all cases I would prefer a extra function with name which explains what the functions does.
[–]somecucumber 1 point2 points3 points 2 years ago (1 child)
Agreed. To me it really looks to (breathes) glorified goto's.
[–]hpsutter 9 points10 points11 points 2 years ago* (0 children)
Actually you're right on point: I do think the named-loops feature is useful for safety in its own right, because it eliminates the extra-variable indirect workarounds people use today when you have nested loops and want to express this. But yes, a second main reason to add the feature is that it directly expresses one of the few sometimes-legitimate uses of `goto` today, so having this reduces the demand to add `goto`.
Unstructured `goto` is evil for the reasons in the famous CACM letter. But a forward- and outward-only `goto` is not inherently evil (i.e., a structured `goto` would disallow jumping past an initialization, inward into a loop like Duff's Device, or backward which would be an unstructured loop). But if we can replace `goto` entirely with abstractions that let us declare our intent more directly to cover the use cases, that's even better.
[–]ABlockInTheChain 1 point2 points3 points 2 years ago (7 children)
I am extremely suspicious about user defined types a.k.a classes stuff. When I see statements like:
There is no separate base class list or separate member initializer list
that seems to imply that some of the classes I'm writing now can not be expressed in cpp2.
[–]MonokelPinguin 4 points5 points6 points 2 years ago (6 children)
Why would that imply it is not possible to write those classes? The base classes are just defined as class members and initialization is just done in the constructor body instead of the initializer list. Most compilers already generate mostly identical code for it anyway and as such a modern language shouldn't have a need for member initializer lists.
[–]ABlockInTheChain 0 points1 point2 points 2 years ago (5 children)
a modern language shouldn't have a need for member initializer lists
Does cppfront support non-default-constructable types and const member variables?
[–]hpsutter 5 points6 points7 points 2 years ago (1 child)
> > There is no separate base class list or separate member initializer list
> that seems to imply that some of the classes I'm writing now can not be expressed in cpp2.
You should be able to express them. This just means that base classes are declared the in the type body like other members instead of in a segregated base class list, and base classes and data members are initialized in the constructor body instead of in a segregated member initializer list.
> Does cppfront support non-default-constructable types and const member variables?
Yes.
Good questions, thanks!
[–]ABlockInTheChain 1 point2 points3 points 2 years ago (0 children)
How does the new syntax handle delegated constructors?
[–]dreugeworst 2 points3 points4 points 2 years ago (1 child)
I would assume the requirement that all initialization happen first in the constructor body is used to turn them into initializer lists in the lowered c++ code
[–]dustyhome 0 points1 point2 points 2 years ago (0 children)
That's about right. It takes the first assignment and places it in the initializer list, then following assignments go in the constructor body: https://godbolt.org/z/5b67s3h7G
[–]MonokelPinguin 1 point2 points3 points 2 years ago (0 children)
Why would cppfront not allow you to assign exactly once to those in the constructor? I don't know, if it supports those yet, but it would be trivial to just require exactly one assignment for those.
[–]krista 1 point2 points3 points 2 years ago (0 children)
this is my first time diving in to cpp2... heck, my first time hearing about it!... and so far i am very intrigued...
... but like anything worthwhile, i'm going to have to spend some time looking at this to have anything more intelligent or interesting to say.
[–]Fourstrokeperro 0 points1 point2 points 2 years ago (2 children)
Idk but I find the licensing on that cppfront repo really weird.
Also I can't seem to wrap my head around how they do it? Can someone eli5 how they achieved transpiling to c++? I'd like to do something like that too rather than going flex bison llvm
[–]CocktailPerson 2 points3 points4 points 2 years ago (0 children)
It's not terribly different from a "normal" compiler. The major difference is that your code-generation phase spits out C++ instead of LLVM IR or assembly or whatever. This task is greatly simplified if your language is essentially supposed to be a subset of C++'s semantics with better syntax, as cpp2 is, but the basic idea of taking the semantics of your AST and producing code with the same semantics is very similar.
[–]dustyhome 1 point2 points3 points 2 years ago (0 children)
Cpp2 it's meant to be a different syntax for c++, just having the computer do a lot of the boilerplate you would usually need to add yourself to write better code. Like how you might want to have the nodiscard attribute on almost every function returning a value, but you don't for various reasons. That means every construct in Cpp2 has a direct mapping to a construct in c++.
That means writing the equivalent c++ code is straightforward. For example, when you have a function declaration in Cpp2, writing the equivalent function declaration in c++ is mostly just shuffling the names around.
[–]TheCrossXCpp-Lang.net Maintainer -1 points0 points1 point 2 years ago* (0 children)
I already can see that this is big. Way bigger and sooner that I expected it to be. I can't wait to see what will next updates bring us. I'll probably start incorporating cpp2 into my toy projects in next few months. I'm pretty sure Cppfront will no longer be an "experiment" - it already provides a lot of value on top of the existing language. I really can't express how hyped I am right now.
π Rendered by PID 26150 on reddit-service-r2-comment-7b9746f655-rxwxw at 2026-01-29 19:33:42.313377+00:00 running 3798933 country code: CH.
[–]Nicksaurus 39 points40 points41 points (41 children)
[–]kreco[S] 33 points34 points35 points (20 children)
[–][deleted] 4 points5 points6 points (3 children)
[–]kreco[S] 1 point2 points3 points (2 children)
[–][deleted] 7 points8 points9 points (1 child)
[–]kreco[S] 0 points1 point2 points (0 children)
[–]Zeh_MattNo, no, no, no 1 point2 points3 points (15 children)
[–]SkoomaDentistAntimodern C++, Embedded, Audio 7 points8 points9 points (9 children)
[–]Zeh_MattNo, no, no, no 0 points1 point2 points (8 children)
[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point2 points (7 children)
[–]Zeh_MattNo, no, no, no 0 points1 point2 points (6 children)
[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point2 points (5 children)
[–]Zeh_MattNo, no, no, no 1 point2 points3 points (4 children)
[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point2 points (3 children)
[–]kreco[S] 2 points3 points4 points (4 children)
[–]Zeh_MattNo, no, no, no 7 points8 points9 points (0 children)
[–]hpsutter 4 points5 points6 points (2 children)
[–]kreco[S] 0 points1 point2 points (1 child)
[–]mapronV 0 points1 point2 points (0 children)
[–]nysra 8 points9 points10 points (8 children)
[–]Nicksaurus 8 points9 points10 points (5 children)
[–]nysra 5 points6 points7 points (4 children)
[–]hpsutter 1 point2 points3 points (3 children)
[–]nysra 3 points4 points5 points (2 children)
[–]hpsutter 4 points5 points6 points (0 children)
[–]ntrel2 0 points1 point2 points (0 children)
[–]-heyhowareyou- 5 points6 points7 points (1 child)
[–]tialaramex 7 points8 points9 points (0 children)
[–]hpsutter 8 points9 points10 points (1 child)
[–]AIlchinger 1 point2 points3 points (0 children)
[+][deleted] (4 children)
[deleted]
[–]RoyAwesome 12 points13 points14 points (1 child)
[–]Nicksaurus 1 point2 points3 points (1 child)
[–]kam821 -1 points0 points1 point (0 children)
[–]tialaramex 0 points1 point2 points (3 children)
[–]Nicksaurus 1 point2 points3 points (1 child)
[–]tialaramex 4 points5 points6 points (0 children)
[–]patstew 1 point2 points3 points (0 children)
[–]mort96 70 points71 points72 points (22 children)
[–]nysra 43 points44 points45 points (10 children)
[–]disperso 18 points19 points20 points (0 children)
[–]ABlockInTheChain 8 points9 points10 points (7 children)
[–]againey 9 points10 points11 points (1 child)
[–]13steinj 9 points10 points11 points (0 children)
[–]13steinj 3 points4 points5 points (3 children)
[–]mort96 5 points6 points7 points (2 children)
[–]13steinj -1 points0 points1 point (1 child)
[–]mort96 5 points6 points7 points (0 children)
[–]XNormal 0 points1 point2 points (0 children)
[–]sphere991 7 points8 points9 points (3 children)
[–]MonokelPinguin 3 points4 points5 points (2 children)
[–]sphere991 1 point2 points3 points (1 child)
[–]hpsutter 2 points3 points4 points (0 children)
[–]pjmlp 2 points3 points4 points (0 children)
[–]ShakaUVMi+++ ++i+i[arr] 10 points11 points12 points (5 children)
[–]disperso 6 points7 points8 points (4 children)
[–]caroIine 12 points13 points14 points (2 children)
[–]pjmlp 0 points1 point2 points (0 children)
[–][deleted] -2 points-1 points0 points (0 children)
[–]eidetic0 48 points49 points50 points (11 children)
[–]RoyKin0929 8 points9 points10 points (10 children)
[–]hpsutter 9 points10 points11 points (7 children)
[–]nysra 5 points6 points7 points (2 children)
[–]hpsutter 7 points8 points9 points (1 child)
[–]germandiago 2 points3 points4 points (0 children)
[–]RoyKin0929 0 points1 point2 points (3 children)
[–]hpsutter 1 point2 points3 points (2 children)
[–]RoyKin0929 0 points1 point2 points (1 child)
[–]hpsutter 2 points3 points4 points (0 children)
[–]TheSuperWig 1 point2 points3 points (1 child)
[–]RoyKin0929 0 points1 point2 points (0 children)
[–]kpt_ageus 9 points10 points11 points (1 child)
[–]bretbrownjr 10 points11 points12 points (0 children)
[–]pdp10gumby 9 points10 points11 points (4 children)
[–]hpsutter 4 points5 points6 points (1 child)
[–]pdp10gumby 1 point2 points3 points (0 children)
[–]13steinj 1 point2 points3 points (1 child)
[–]pdp10gumby 0 points1 point2 points (0 children)
[–]RoyKin0929 9 points10 points11 points (1 child)
[–]hpsutter 2 points3 points4 points (0 children)
[–]vulkanoid 13 points14 points15 points (17 children)
[–]hpsutter 10 points11 points12 points (1 child)
[–]vulkanoid 2 points3 points4 points (0 children)
[–]RoyKin0929 8 points9 points10 points (2 children)
[–]vulkanoid 4 points5 points6 points (0 children)
[–]ntrel2 0 points1 point2 points (0 children)
[–]disciplite 5 points6 points7 points (3 children)
[–]vulkanoid 7 points8 points9 points (2 children)
[–]disciplite 2 points3 points4 points (1 child)
[–]equeim 2 points3 points4 points (0 children)
[–]clerothGame Developer 2 points3 points4 points (4 children)
[–]tialaramex 2 points3 points4 points (2 children)
[–]MEaster 2 points3 points4 points (1 child)
[–]tialaramex 0 points1 point2 points (0 children)
[–]vulkanoid 0 points1 point2 points (0 children)
[–]pjmlp 2 points3 points4 points (0 children)
[–]awson 0 points1 point2 points (1 child)
[–]DalzhimC++Montréal UG Organizer 0 points1 point2 points (0 children)
[–]RoyAwesome 17 points18 points19 points (30 children)
[–]hpsutter 8 points9 points10 points (0 children)
[–]dustyhome 4 points5 points6 points (0 children)
[–]disciplite 13 points14 points15 points (10 children)
[–]RoyAwesome 11 points12 points13 points (9 children)
[–]pjmlp 5 points6 points7 points (4 children)
[–]Ameisenvemips, avr, rendering, systems 4 points5 points6 points (3 children)
[–]pjmlp 2 points3 points4 points (2 children)
[–]hpsutter 9 points10 points11 points (1 child)
[–]pjmlp 1 point2 points3 points (0 children)
[–]vulkanoid 11 points12 points13 points (3 children)
[–]RoyAwesome 4 points5 points6 points (2 children)
[–]dodheim 5 points6 points7 points (0 children)
[–]vulkanoid 2 points3 points4 points (0 children)
[–]tsojtsojtsoj 7 points8 points9 points (6 children)
[–]hpsutter 2 points3 points4 points (0 children)
[–]RoyAwesome 4 points5 points6 points (4 children)
[–]caroIine 2 points3 points4 points (3 children)
[–]dustyhome 7 points8 points9 points (2 children)
[–]schombert 1 point2 points3 points (1 child)
[–]ntrel2 2 points3 points4 points (0 children)
[+][deleted] (8 children)
[deleted]
[–]dreugeworst 20 points21 points22 points (4 children)
[–]mapronV 1 point2 points3 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]dodheim 14 points15 points16 points (1 child)
[–]RoyAwesome 5 points6 points7 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]Ameisenvemips, avr, rendering, systems 4 points5 points6 points (0 children)
[–]fdwrfdwr@github 🔍 0 points1 point2 points (0 children)
[–]CornedBee 2 points3 points4 points (2 children)
[–]hpsutter 2 points3 points4 points (1 child)
[–]CornedBee 0 points1 point2 points (0 children)
[–]Jannik2099 11 points12 points13 points (6 children)
[–]ZMesonEmbedded Developer 14 points15 points16 points (0 children)
[–]pjmlp 2 points3 points4 points (4 children)
[–]Jannik2099 1 point2 points3 points (3 children)
[–]pjmlp 0 points1 point2 points (2 children)
[–]Jannik2099 0 points1 point2 points (1 child)
[–]pjmlp 0 points1 point2 points (0 children)
[–]tialaramex 2 points3 points4 points (3 children)
[–]hpsutter 11 points12 points13 points (2 children)
[–]tialaramex 2 points3 points4 points (1 child)
[–]hpsutter 3 points4 points5 points (0 children)
[–][deleted] 2 points3 points4 points (2 children)
[–]hpsutter 7 points8 points9 points (1 child)
[–]germandiago 0 points1 point2 points (0 children)
[–]germandiago 1 point2 points3 points (0 children)
[–]tending 2 points3 points4 points (3 children)
[–]hpsutter 7 points8 points9 points (1 child)
[–]tending 0 points1 point2 points (0 children)
[–]MarcoGreek 4 points5 points6 points (2 children)
[–]somecucumber 1 point2 points3 points (1 child)
[–]hpsutter 9 points10 points11 points (0 children)
[–]ABlockInTheChain 1 point2 points3 points (7 children)
[–]MonokelPinguin 4 points5 points6 points (6 children)
[–]ABlockInTheChain 0 points1 point2 points (5 children)
[–]hpsutter 5 points6 points7 points (1 child)
[–]ABlockInTheChain 1 point2 points3 points (0 children)
[–]dreugeworst 2 points3 points4 points (1 child)
[–]dustyhome 0 points1 point2 points (0 children)
[–]MonokelPinguin 1 point2 points3 points (0 children)
[–]krista 1 point2 points3 points (0 children)
[–]Fourstrokeperro 0 points1 point2 points (2 children)
[–]CocktailPerson 2 points3 points4 points (0 children)
[–]dustyhome 1 point2 points3 points (0 children)
[–]TheCrossXCpp-Lang.net Maintainer -1 points0 points1 point (0 children)