[HN Gopher] Overview: What are Cpp2 and cppfront? How do I get a...
___________________________________________________________________
Overview: What are Cpp2 and cppfront? How do I get and build
cppfront?
Author : cyber1
Score : 183 points
Date : 2024-03-31 21:26 UTC (1 days ago)
(HTM) web link (hsutter.github.io)
(TXT) w3m dump (hsutter.github.io)
| darknavi wrote:
| I know some of the arguments around C++/cpp2 are flawed, but I do
| love C++ and a project to move it forward with a step-change is
| exciting to me.
|
| A few of my friends and I did Advent of Code in cpp2 this year
| and it was a (very buggy) blast.
| Galanwe wrote:
| > A few of my friends and I did Advent of Code in cpp2 this
| year and it was a (very buggy) blast.
|
| You mean buggy ironically because you were discovering cpp2, or
| because cpp2 itself was buggy?
| JNRowe wrote:
| Sutter's 2022 cppcon talk1 is a _great_ introduction to the
| topic, both the problems it attempts to solve and solutions it
| was /is settling on. One of those few talks that left me
| genuinely enthused about the topic.
|
| [It is probably worth watching for the Compiler Explorer
| interjection toward the end alone -- Matt Godbolt Appreciation
| Society]
|
| 1 https://www.youtube.com/watch?v=ELeZAKCN4tY
| cies wrote:
| Like Kotlin to Java
|
| Or ReScript to OCaml
|
| Or Gleam to Erlang
| treyd wrote:
| Kotlin is a distinct language with new features that (I'm
| fairly sure) make it non-isomorphic to Java code though. But I
| can't speak on the others.
| simon_void wrote:
| not sure what "(non)-isomorphic" but Kotlin code is
| interoperable with Java code in both directions. You can
| easily call Java-code from Kotlin (but I guess that's the
| easy direction) but you can also call Kotlin code from Java.
| There exist specific annotations to control how Kotlin code
| will be converted to bytecode and therefore be invoked from
| Java, e.g. a function in a Kotlin companion object would
| normally concerted into a function of a Singleton property
| called INSTANCE on the base class, but if you annotate it
| with @JvmStatic it will become a static method of that class
| in bytecode instead. This means you can write a Kotlin lib
| that feels very normal to call from Java. here's the relevant
| part of the documentation: https://kotlinlang.org/docs/java-
| to-kotlin-interop.html So yes, Kotlin is considered to be a
| successor language, not just another language (also)
| compiling to the JVM.
| treyd wrote:
| An isomorphism is a structure preserving 1:1 map between
| two sets or some other pair of structures. While Java and
| Kotlin are interoperable (because they both target the
| JVM), they are not isomorphic for precisely the reasons you
| described. If it was, then you could do round trip machine
| translation on the syntax in either direction and have a
| result that's identical to the original. You can't do that
| because Kotlin extends Java in nontrivial semantic ways
| (for a concrete example, see Nothing in the article you
| linked).
|
| Cpp2 isn't trying to be a successor language to C++, the
| article states that it's trying to present an identical
| feature set in a new skin where the best practices of
| modern C++ are more ergonomic without introducing any new
| functionality.
| tialaramex wrote:
| > Cpp2 isn't trying to be a successor language to C++
|
| Herb is trying to _sell_ it as not a successor because
| for now that suits him better.
|
| Because C++ is a general purpose language Herb can
| deliver a "not a new feature" that is so elaborate in
| practice you would never write the equivalent C++ by
| hand, but Herb can insist that since technically it can
| still be transpiled to C++ it's not a different
| language... right up until that ceases to suit his
| agenda.
|
| Under this model WUFFS (a higher performance yet entirely
| safe language for writing stuff like codecs and
| compression algorithms) doesn't "introduce any new
| functionality" compared to C, since the present WUFFS-
| the-language is transpiled into C. But I think C
| programmers would be astonished to hear that _C_ is now
| apparently higher performance than C and is able to
| guarantee it 's entirely safe...
| treyd wrote:
| The linked article explicitly states
|
| > What it isn't. Cpp2 is not a successor or alternate
| language [...]
| tialaramex wrote:
| That's correct, Herb Sutter does indeed claim that his
| C++ Successor Language isn't a C++ Successor Language, as
| I stated.
| Cerium wrote:
| Or, Cfront to C? :)
| jokoon wrote:
| or more famously, typescript to javascript, which is the
| example Herb uses.
| Zambyte wrote:
| Surprised to see Gleam instead of Elixir, I haven't heard of
| the former before.
| zem wrote:
| all of those compile to the respective languages' bytecodes,
| not to source code
| superamadeus wrote:
| Is there any discussion or in-depth explanation of the syntax
| choices? I understand that a goal was context free unambiguous
| parsing. But there are some things that surprise me.
|
| For example, string interpolation: "Hello,
| (msg)$!\n"
|
| Why "(msg)$" and not "$(msg)"? Surely the latter is easier to
| parse?
| jiripospisil wrote:
| https://github.com/hsutter/cppfront/wiki/Design-note:-Captur...
| loeg wrote:
| Raises more questions honestly. This looks more different
| from today's C++ than Rust.
| epage wrote:
| I feel like consistency goes too for losing the visual
| parsing benefits of special syntax. Otherwise, we might as
| well adopt lisp's syntax.
| breatheoften wrote:
| What does "for later use" mean?
|
| One thing I've noticed recently is that pretty much no
| language has a good way to simultaneously define a nested
| structure and assign that structure a name "for later reuse".
|
| For example -- suppose I'm dealing with some serialized data
| structure that come from some external system. Very likely
| the data model behind this value involves "nested values"
| which have themselves have some type of which might be reused
| in multiple places by that external system.
|
| When the goal is to just solve problems -- the approach i
| like to take is to focus on the values i want to consume and
| produce -- which might themselves contain lots of nested
| types each with some amount of reuse ...
|
| I'd really like a language feature that supports
| simultaneously defining a type where it's relevant within
| some other data structure _and also allows_ giving that
| embedded thing a name for independent reuse ...
|
| I wonder if this postfix $ syntax is related to that use case
| at all ...
|
| (... this comment a speculation based on names of things only
| without even reading the whole article ...)
| superamadeus wrote:
| Oh, fantastic. Thank you!
| ralferoo wrote:
| I think $() makes much more sense than ()$ in terms of
| parsing, when you see $ you're looking for a single word
| token, and $( you're parsing up to the matching )
|
| Maybe it's not much extra workload, but ()$ requires you to
| pattern-match all bracketed content in a string as a possible
| capture, and the parser can't determine whether it's a
| capture or not until the $ or a matching close bracket.
| Consider parsing "((x)$)" would require parsing the entire
| string to determine the first bracket wasn't a capture, then
| we can treat just the first character as a literal, then we
| have to re-parse the rest of the string again, and can't be
| sure it's a capture until the $, and then we can evaluate it
| as a real expression and then continue with the final $.
|
| Other interesting cases would be "(x+(x)$)" or even
| "((x+(x)$)$)". I'm not sure I could easily predict the
| parsing of latter, but prefix and permitted unbracketed
| single word variable names "(x+$x)" is clearer and the second
| would either be "(x+$(x)$)" or "($(x+$(x)))" both of which
| are explicit using the prefix form and could simplify to
| "(x+$x$)" and "($(x+$x))" where the intent seems clearer.
|
| There's the precedent that many languages already use the
| prefix form which would help newcomers with familiarity, and
| in fact many of these languages wouldn't even need the
| brackets and $var would be sufficient, and I can't really see
| why the spec requires the brackets in the ()$ syntax in
| string literals but not for expressions in code.
| OskarS wrote:
| > Maybe it's not much extra workload, but ()$ requires you
| to pattern-match all bracketed content in a string as a
| possible capture, and the parser can't determine whether
| it's a capture or not until the $ or a matching close
| bracket.
|
| You don't have to: you can also just parse the string
| backwards.
| lifthrasiir wrote:
| That raises more questions though... Like, why does the
| capture syntax have to be attached to the captured element?
|
| Rust for example has a single `move` syntax for all-capture
| vs. no-capture toggle, e.g. `|x| x + foo` (`foo` is stored as
| a reference) vs. `move |x| x + foo` (`foo` is moved into the
| closure). While I do want an additional mode for uniformly
| applying specific methods (typically `.clone()`) for captured
| elements, that is almost enough for typical closures.
|
| Also, if my reading of the documentation is correct, `$` has
| to be attached to _each_ occurrence of captured elements.
| Like, `:(i) = i + foo$ * (foo$ + 1)`. Doesn 't that look
| strange? It is even possible to mix two variants of captures
| like `:(i) = i + foo$ * (foo&$* + 1)`, and it's not entirely
| obvious to me what will happen to `foo$` when `foo&$*` is
| updated. Treating these "upvalues" as a sort of an implicit
| structure (e.g. `$.foo`) is much more consistent, and a
| prefix form `$foo` can be regarded as its shorthand.
| dataflow wrote:
| I feel like there are more options than just the ones he
| listed. Like "Hello ${name}" or "Hello `name`" or "Hello
| {`name`}"...
| zarzavat wrote:
| Something about foolish consistencies...
|
| The priorities of a programming language syntax are to be
| readable first, consistent second.
|
| When it comes to syntax that is used more frequently then the
| importance of readability increases, and the importance of
| consistency drops, because people will become familiar with
| the syntax through frequency of use, so there is no need for
| them to be guided by consistency. Yet they will be reading
| the code more often.
|
| When it comes to syntax that is used infrequently then
| consistency is of more importance because you want users to
| be able to intuit the syntax. Since it will be infrequently
| used, the readability is of lesser impact.
| colonwqbang wrote:
| Most of my personal issues aren't with C++ syntax as such
| (although it also has many problems). My main gripes are:
|
| 1. Very slow compilation.
|
| 2. Poor encapsulation, adding private functions requires
| recompiling all dependents, see (1).
|
| 3. Comically huge symbols make debugging much harder than it
| needs to be -- today gdb OOM'd my 16GB laptop when trying to form
| a backtrace of a typical QT application coredump.
|
| Unfortunately it doesn't seem like cppfront can fix these issues.
| It may still be a worthwhile effort in other respects, of course.
| ThouYS wrote:
| under what circumstances does (2) hold? for vanilla methods
| it's no problem
| loeg wrote:
| If you touch a header, even private only, includers will
| rebuild. Modules might fix this.
| phkahler wrote:
| >>> 2. Poor encapsulation, adding private functions requires
| recompiling all dependents
|
| >> under what circumstances does (2) hold?
|
| To add a private member variable or function, you need to put
| it in the class definition in the header file. Then anything
| that includes the header needs to be recompiled.
| ThouYS wrote:
| they don't need to be. dependents will continue
| functioning, because ABI hasn't changed
| jstimpfle wrote:
| Now explain that to my build system.
|
| But even if you managed to do that, first compilation is
| still much slower than it should be, because anlot of
| headers have to be included (transitively) to allow even
| declaring these fields and methods.
| hkwerf wrote:
| Admittedly, adding a private member _variable_ changes the
| object size and thus the ABI and thus requires
| recompilation of dependencies.
|
| Thinking about that, is there any case in which private
| functions can end up in a vtable? In that case, it'd break
| ABI too.
| NekkoDroid wrote:
| > is there any case in which private functions can end up
| in a vtable?
|
| Yes, but it generally isn't something that is done.
|
| https://godbolt.org/z/5oPovKzoT
| colonwqbang wrote:
| C++ build systems are typically based on file timestamps.
| Modifying a header file triggers recompilation of all
| translation units including that header.
|
| There are workarounds like pimpl (aka. C style
| encapsulation). But this requires extra boilerplate and
| indirection. C++ modules might fix it at some point, but
| after 35 years of not having them in C++ most real life
| codebases aren't set up that way and may never be.
| gumby wrote:
| I don't think of pimpl as a tool for speeding up
| compilation, but for black box encapsulation.
|
| If the compile time (when adding a method) is really an
| issue you can chop up and reconfigure your include files. A
| pain, but perhaps saves you time in the long run.
|
| Of course ( _waves hands_ ) modules will magically improve
| things...someday.
| colonwqbang wrote:
| I think it can be both things.
|
| Haven't you ever seen someone do struct
| Thing; struct OtherThing;
|
| in lieu of just including "thing.h"? I see it frequently
| in real life code bases and I can't see a reason for it
| other than compilation time optimisation.
| gumby wrote:
| Sure, I do that all the time too. But you can't call a
| method (or look inside Thing, or pass it as an argument,
| only a pointer to it) without including the definition.
|
| Hmm, there might be some interesting linker hacks to
| patch things up post compilation. But then you'd want
| some way to do the forward declaration for cases where
| Thing could have been passed in registers...
| DerekL wrote:
| This is sometimes required to break dependency cycles.
| Also, you can use this to rearrange declarations in the
| same file.
| germandiago wrote:
| Pimpl is both. I use it as black box also, but it does
| both things.
| jstimpfle wrote:
| Adding private functions or fields requires changing the
| class declarations, which requires rebuilding any code that
| includes that class deckaration. It shouldn't be like that,
| especially for methods which shouldnt change anything about
| the class ABI.
|
| Even worse, this dependency is transitive. Dependencies to
| allow defining these private methods an fields are exposed
| too, forcing inclusion of headers to all members of the
| class, even if it's only implementation details.
| MindSpunk wrote:
| Adding methods changes the vtable layout so there's no way to
| not recompile all the dependents. There's no solution to this
| unless private functions are guaranteed to not be virtual.
| AnimalMuppet wrote:
| Um... isn't that guaranteed? What would it mean for a private
| function to be virtual? It can't be overridden by a different
| implementation in a child class...
| zetafunction wrote:
| `final` prevents a child class from overriding a method.
| `private` does not.
| Findecanor wrote:
| Yes, you can have a private virtual member function, and it
| can be overridden in a child class (unless declared final),
| apparently.
|
| I too thought that sounded insane, so I just looked it up.
| I've been programming C++ for twenty five years and the
| thought of wanting to do this have never ever occurred to
| me...
| AnimalMuppet wrote:
| TIL.
| usefulcat wrote:
| It could make sense if you have a method that you want
| derived classes to be able to override but not to be able
| to call directly.
|
| This is, admittedly, a pretty niche case but certainly
| not inconceivable.
| zetafunction wrote:
| Except the derived class can simply change the visibility
| of the override, so...
| bregma wrote:
| Child classes can override private member functions in
| their parent class.
|
| Making all virtual functions private is the better way to
| implement inheritance: it separates implementation from
| interface, allows for common functionality to be moved to
| the base class without requiring gymnastics in each and
| every derived class, and even lets you simplify the public
| interface to reduce compile times.
|
| Try it, you'll like it. You may never go back to writing
| Java in C++.
| PoignardAzur wrote:
| Wait, what? Virtual methods in C++ are opt-in. You only need
| the 'final' keyword when you're overriding a method in a
| child class.
| josephg wrote:
| There isn't _that_ much work the compiler should need to do.
| Ideally, a clever compiler could just update the vtables in
| some intermediate representation of the program and re-emit
| the binary. The C++ compiler today is so insanely wasteful -
| edit one header file and the compiler re-parses all the
| header files N^2 times. It should be possible to make a
| compiler thats way faster than any C++ compiler today.
|
| Of course, the elephant in the room is the C++ preprocessor.
| I haven't looked too closely into cppfront, but if I had the
| chance, I'd give the C macro system a bullet.
|
| My favorite "macro" system by far is zig's comptime, which is
| beautiful and elegant. Zig code can simply elect to be
| executed in the compiler instead of at runtime. For example,
| here's how the print() function compiles in zig. Its a thing
| of beauty:
|
| https://ziglang.org/documentation/master/#Case-Study-
| print-i...
| edflsafoiewq wrote:
| The problem is reflection, including SFINAE. Code can
| branch on if a method exists.
| josephg wrote:
| So? It shouldn't take seconds to make one change to the
| reflection database. Or update derived code.
|
| Incremental compilation is fundamentally the same problem
| that web frameworks solve. There's plenty of efficient
| ways to do it.
| vitus wrote:
| > The C++ compiler today is so insanely wasteful - edit one
| header file and the compiler re-parses all the header files
| N^2 times. It should be possible to make a compiler thats
| way faster than any C++ compiler today.
|
| Well, yeah, that's the problem that modules are supposed to
| solve. (AFAIK the only fully C++20-standard-compliant
| implementation of modules is in MSVC, although even Clang
| modules are adequate for drastically reducing compile
| times.)
|
| You are still going to have to recompile all your dependent
| compilation units if the vtable layout changes, as
| mentioned elsewhere, since all of your callsites have to
| reflect any changes to vtable lookups.
|
| > My favorite "macro" system by far is zig's comptime,
| which is beautiful and elegant. Zig code can simply elect
| to be executed in the compiler instead of at runtime. For
| example, here's how the print() function compiles in zig.
| Its a thing of beauty:
|
| How is this different from constexpr / consteval in C++?
|
| std::format (introduced in C++20) is implemented in a
| similar fashion in that the format string is checked at
| compile-time (number of args, types, etc), so there's no
| good reason why C++ couldn't have a print function that
| behaves the same way and is validated at compile-time [0].
| Libraries such Abseil and folly certainly provide this.
|
| [0] It seems that C++23's std::print is not that function,
| oddly enough.
| inkyoto wrote:
| Yes, there are solutions, and the simplest one is replace the
| direct vtable lookup with an indirect one where the methods
| are referenced not by their numeric offsets in the vtable but
| via their symbolic names and the offset resolution is
| performed via a separate lookup table - not that dissimilar
| from how it is done in ELF shared libraries.
|
| All of them will result in incurring a performance penalty,
| either at the runtime, or at the start-up time, or in the
| compiler/linker, and memory blowouts. But it will solve the
| recompilation problem.
| Too wrote:
| In typical c++ manner, we can't have nice things because
| {obscure internal technicality that don't impact the user in
| any way whatsoever}$. As a user I don't care what a vtable
| is. Just make it work. Other languages don't have this
| problem.
|
| During release builds sure, optimize away with LTO and
| whatever is needed to make it vroom. During development,
| waiting several minutes for a minor private function update
| is just absurd.
| germandiago wrote:
| C++ is designed for good performance. Solving ABI problems
| permanently usually means an extra layer of indirection,
| which hurts performance.
|
| There are also concerns about what you can link against or
| not once you modify the ABI of widely used types. This is
| of real concern at least for closed-source software or
| software that just cannot be recompiled.
|
| I am not saying it should be the right choice, some of that
| software is legacy. I just say this is a real concern.
|
| As for the private function. You can use a pimpl and use a
| private (on the cop file instead of header) and use that,
| for example, in many cases. This keeps compile time down.
| jokoon wrote:
| I laughed at (3)
|
| Although QT is not a tiny framework, and I don't really know if
| modern C++ tools are really good enough for this sort of
| problem, since C++11 to 20 probably caused those tools to
| explode in memory consumption
|
| But I am not surprised at all. I remember around 2013, I would
| use bullet physics and the Ogre3D engine, and I had to tell
| visual C++ to increase its memory capacity because the compiler
| would refuse to continue.
| a_t48 wrote:
| 3) is forcing me to incorporate a symbol strip/upload to
| sentry piping at $WORK because Eigen debug symbols cause our
| build sizes to explode.
| jmac01 wrote:
| How is dependency management not in this list hahaha holy crap
| c++ is so fucking shit to work with in this regard compared to
| another "modern" language.
| pciexpgpu wrote:
| I wonder how this compares with Carbon -> C++ [0].
|
| Carbon is (was?) a fantastic proposal, but not sure if it has
| lost steam since it was introduced or how well it is being
| adopted (be it inside Google or outside)?
|
| Being able to incrementally/interchangeably use/call existing C++
| code (and vice versa) seems like a great design choice (in
| Carbon) without having to introspect the actual generated code.
|
| Not sure how easy it is to get the cppfront-generated C++ to
| bridge with existing C++ code (and vice versa)?
|
| [0] https://github.com/carbon-language/carbon-lang
| mort96 wrote:
| Carbon isn't "being adopted", it's still being developed.
| Making a programming language takes time. Let them at least get
| to a point where they release some form of public beta (i.e a
| few more years, at least) before talking about adoption.
| nindalf wrote:
| The roadmap for Carbon [0] mentions wanting to have basic, non-
| trivial programs written in Carbon by the end of 2024. They're
| aiming for a v0.1 release in 2025. If it gains traction,
| they're aiming for a v1.0 beyond 2027.
|
| I don't think anyone outside Google will seriously adopt this
| before it reaches v1.0. Even within Google, they may choose
| other options.
|
| [0] - https://github.com/carbon-language/carbon-
| lang/blob/trunk/do...
| bluGill wrote:
| Googles habit of dropping support for useful things make me
| not willing to trust them. I plan for my current code to be
| in use for at least 20 more years (i plan to retire before
| then), I don't want to explain to my boss either why we are
| maintaining a compiler or why we must rewrite working code.
| fractalb wrote:
| I suspect you don't touch Go code.
| jokoon wrote:
| Herb Sutter briefly talked about why carbon was not a good
| contender, and I think remembering it was because of backward
| compatibility with C++.
|
| At some point, keeping C++ semantics matters, since having
| different semantics would obviously prevent using previous C++
| codebases, or make it more difficult to make those work
| together, and that may be why Carbon may not be a good choice.
| suby wrote:
| I spent the other day writing an archetype entity component
| system which made heavy use of template metaprogramming. I try to
| avoid this if possible, but it was an exercise in seeing how
| performant I could make it, and so I wanted to offload as much
| work as I could manage to compile time and avoid things like
| virtual dispatch. API similar to the basic parts of entt.
|
| My take away from the exercise is that this is not a language for
| human beings. I was successful in writing it, but it was
| extremely difficult and frustrating. Part of the frustration is
| because conceptually what I wanted to accomplish was not
| difficult, but figuring out how to express it was a nightmare. I
| am not new to the language, I've been writing C++ since 2009, it
| was the first language I learned and I've spent nearly every day
| of my life since then writing at least some C++ code. Even so, I
| can't say that I truly understand this shit.
|
| I'm hoping cpp2 brings us someplace closer to a language that
| mere mortals can understand. I don't want the next generation
| writing C++.
| Kwpolska wrote:
| There are lots of languages for mere mortals. They are not
| compatible with C++, but that's a feature. The next generation
| is largely not writing C++.
| logicchains wrote:
| >I spent the other day writing an archetype entity component
| system which made heavy use of template metaprogramming. >I'm
| hoping cpp2 brings us someplace closer to a language that mere
| mortals can understand.
|
| Are you sure the problem is the language itself, and not the
| inherent complexity of that kind of metaprogramming? As far as
| I'm aware, Lisp is the language with the cleanest support for
| such metaprogramming, yet metaprogramming-heavy Lisp code is
| still quite hard to read. I'm not aware of a programming
| language in which a compile-time ECS would be easy to
| write/read.
| crazypython wrote:
| What are the tradeoffs between cpp2 and Carbon?
| Aardwolf wrote:
| Unfortunately the first example already re-uses one of the less
| good parts of C++, the "<<" operator for std::cout, which always
| was a bit of a hack (including strange order of operations since
| << normally is left shift)
| kreco wrote:
| Note that the "<<" operator for std::cout is not related to the
| language itself but related to the standard library.
| Sharlin wrote:
| But in a "C++ 2" helloworld one would really expect to see
| std::println used instead [1].
|
| [1] https://en.cppreference.com/w/cpp/io/println
| tialaramex wrote:
| Cpp2 is part of the trend for would be "C++ Successor
| languages" from 2022. The std::println function was
| standardised in C++ 23, and implementations still don't all
| provide it in full today.
|
| So in effect Cpp2 pre-dates std::println. Herb will have
| been aware that it exists and is likely for C++ 23, but it
| doesn't make sense to ship software which requires features
| you suspect won't be widely available for several years. A
| "Hello, world" program should not be relying on bleeding
| edge features.
| userbinator wrote:
| I've always thought that using "<<" in that manner was more of
| a way to show off the operator overloading feature than
| anything else.
| OskarS wrote:
| I think the original point was to make a typesafe printf
| that's still reasonably efficient without creating a bunch of
| small temporary strings. Like, if you printf like this:
| printf("Number %d, String %s", n, s);
|
| but the types of n and s aren't int and char*, all hell
| breaks loose and you have the origin of a million CVEs. But
| how do you make that function signature typesafe, without the
| tools of modern templates? You sort of can't. One thing you
| can do is do string append stuff, but the syntax then is
| annoying: print("Number " + to_string(n) +
| ", String " + s);
|
| This also creates a bunch of temporary strings, which is not
| ideal (and still relies on operator overloading, btw). In
| this world, using operators for this does make some sense:
| std::cout << "Number: " << n << ", String " << s;
|
| Like, seen from this perspective, it's not the worst idea in
| the world, it does work nicely. The syntax really isn't too
| bad, IMHO (the statefulness part, though, really sucks). It
| also allows you to add formatting for your own types: just
| overload operator<<!
|
| Clearly, properly type-safe std::format is vastly superior,
| but the C++ of the 90s simply didn't have the template
| machinery to make that part of the standard library.
| tialaramex wrote:
| It would have been possible to, as a special case, type
| check the formatting, C compilers began doing that in the
| 1990s IIRC.
| throwway120385 wrote:
| And there are ways of signaling to a compiler that your
| function accepts a format string and a series of typed
| arguments in case you're wrapping a C-style printf
| function.
| layer8 wrote:
| It wouldn't have added support for custom types to
| printf(), which was an important motivation.
| tialaramex wrote:
| That's true, however neither the mechanism to do this in
| I/O streams, nor the weighty mechanism for std::format
| come close to what Rust's derive macro offers for Debug,
| and in my experience asking for so much means you often
| won't get anything because programmers are lazy.
|
| That is, for a custom type in Rust having rudimentary
| Debug output is one word added to the list of derive
| macros, the word "Debug". Almost everybody will write
| that, it's barely effort.
|
| To get that in C++ I/O Streams you need to write an
| operator overload function and must be careful to write
| it correctly. Trivial types usually don't bother, but
| many C++ programmers are comfortable writing this for an
| important type.
|
| For std::format you need to write a _template_ which is
| already pretty scary, and then it needs to implement
| custom parse and format steps, this is because
| std::format allows your custom type to define its own
| custom formatting rules. This is extremely powerful, but
| everybody must pay for this power. I have not seen most
| people bother doing all this at all.
| layer8 wrote:
| You can write operator<< as a free (non-member) function
| yourself for classes that don't implement it.
| Furthermore, it's not necessarily the same output that
| you want for debugging.
| mort96 wrote:
| Don't you think it just serves to highlight that Cpp2 _is_ just
| C++? That it 's explicitly not some new language with C++ FFI
| like Carbon.
| Kranar wrote:
| Not sure what is meant by this statement. Both cpp2 and
| Carbon are different languages, neither of them claim to be
| just C++.
|
| Both languages will have bidirectional compatibility with C++
| so that code written in C++ can be directly accessed from
| Carbon and code written in Carbon can be directly accessed
| from C++. Neither of them use an FFI for compatibility.
| mort96 wrote:
| It's my understanding that Carbon will create wrapper
| Carbon classes around C++ classes and convert data back and
| forth between C++ types and Carbon types at the interface
| boundary when necessary.
| dale_glass wrote:
| I kinda like it, it's comfortable and pragmatic. I don't have
| to look at the docs to figure out what's the format specifier
| for something like a size_t, and you can easily add support for
| whatever might be needed, so you can comfortably do:
| std::cout << "Player is at" << _player_pos;
|
| And get the x/y/z breakdown automatically.
| diath wrote:
| But you can do that with fmtlib/std::format as well when you
| provide formatting for custom types, that just becomes:
| fmt::println("Player is at {}", _player_pos);
| petters wrote:
| Should be possible to implement cout more effectively since
| there is no string parsing. I say "should" because in
| practice it seems to be the other way around. I don't know
| why.
| hermitdev wrote:
| Been a while since I've looked at it, but pretty sure
| std::format validates and parses most (all?) format
| strings at compile time. There should be no runtime
| penalty.
| blacklion wrote:
| And now try to do task which should be as trivial as add 1
| format character: output number in hexadecimal and leave
| stream in same state that was before your code.
|
| It becomes cumbersome very fast.
| kreco wrote:
| > Because ++ and -- always have in-place update semantics, we
| never need to remember "use prefix ++/-- unless you need a copy
| of the old value." If you do need a copy of the old value, just
| take the copy before calling ++/--
|
| I actually wish ++ and -- operators were removed. This would
| simplify everything, nothing to remember whether it's prefix or
| postfix operator, whether it copies something or not, you would
| just do "value += 1" and be done with it.
|
| - Less mental overhead.
|
| - Remove an extra way of doing the same thing.
| conradev wrote:
| That is the direction that Swift went:
| https://github.com/apple/swift-evolution/blob/main/proposals...
| josephg wrote:
| Rust and Go made the same choice. In Go, I think i++ is valid
| - but only as a statement, not an expression.
|
| https://go.dev/doc/faq#inc_dec
| AnimalMuppet wrote:
| But you would break the name! C++ would be a syntax error!
|
| I mean, there are people that already think that...
| kevindamm wrote:
| We could allow overloading operator++ while not defining it
| on anything built-in....
| layer8 wrote:
| But it's C++2, and that would become valid! (C + +2)
| nathanrf wrote:
| Unfortunately, C++ uses ++ and -- for iterators, many of which
| cannot reasonably implement += or -=. This distinction is baked
| into the type system to tell whether or not an iterator
| supports efficient "multiple advance" (e.g. a linked list
| iterator doesn't have += but a pointer into a contiguous vector
| does).
|
| There's no way to fix this in a reverse-compatible way for
| existing code (which is one of the constraints of cpp2- it must
| work with all existing C++ so that it is possible for existing
| projects to migrate regardless of size).
| Rexxar wrote:
| Making ++ or -- a statement that increment the target without
| returning a value should probably be enough for forward
| iterators.
| layer8 wrote:
| A noncopying ++ _could_ be spelled `+= 1` though. So some
| iterators would support `+= 1`, but not `+= 2`. This would be
| vaguely similar to how the null pointer constant was defined
| as an integer constant expression that evaluates to zero:
| Define `+= < _integer constant expression that evaluates to
| one_ >` as the increment operator.
| fancyfredbot wrote:
| It was when debugging a memory leak which occurred because I
| forgot to declare the base class destructor as virtual that I
| started to think C++ was a rather unfriendly language and not
| really designed to be easy to use.
|
| Then a few years later I read the spec for std::launder that I
| realised C++ was not really designed to be understood.
|
| It's a shame because it's actually a rather nice language in some
| ways. Here's hoping that this project or something similar takes
| off and separates the good bits from the bad.
| josephg wrote:
| Yeah... its shocking to me how difficult it is to read the C++
| standard library. Surely, the standard library is written by
| the authors of the language. It should be a positive example of
| how they hope their language is used, right?
|
| Here's the source of C++'s vector class:
|
| https://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a0111...
|
| In comparison, vec in rust. (Note you need to scroll down a few
| pages to start seeing non-trivial functions. There's a lot of
| block comments.):
|
| https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#398
|
| Or list in Go:
|
| https://cs.opensource.google/go/go/+/master:src/container/li...
|
| To my eye, that C++ code is by far the hardest code to read.
| lanza wrote:
| I work on clang and don't know go yet still find the go
| version easier to read.
| jcelerier wrote:
| > It should be a positive example of how they hope their
| language is used, right?
|
| should it though? there's a million ways to learn C++.
| Reading the std code definitely isn't one - technically the
| std could be entirely compiler builtins. If you want to read
| positive examples take A Tour of C++ 3rd edition
| (https://www.amazon.ca/Tour-C-Bjarne-
| Stroustrup/dp/0136816487)
| tialaramex wrote:
| "Do as I say, not as I do" is known to be poor pedagogy.
|
| If you find that expert practitioners don't do the things
| you think students should be doing, it suggests that
| something is wrong and needs fixing. In the standard
| library implementations it's _very obvious_ that something
| is badly wrong, and yet for _decades_ C++ has resisted the
| hard work of fixing it.
| jcelerier wrote:
| Yet thousands of people learned C++ well enough to use it
| in a professional setting, so surely << "Do as I say, not
| as I do" is known to be poor pedagogy. >> does not hold
| in the general case.
|
| > If you find that expert practitioners don't do the
| things you think students should be doing, it suggests
| that something is wrong and needs fixing
|
| It does not and is a very naive world view. In any trade
| expert practitioners' way of working is wildly different
| from what you would learn in a classroom (and generally
| makes said student's hair raise on their head when they
| see. This is not too relevant with C++ though as the std
| implementation "ugliness" is mainly driven by material
| constraints.
|
| Besides in general in programming this is not even
| _possible_. Like, from your argument one wouldn 't be
| able to learn how to use the Win32 API or Cocoa API since
| the operating systems using them are closed-source and
| you cannot see how they are implemented & used by the
| teams who develop these APIs.
| tialaramex wrote:
| No, I reject the claim that "my argument" (not in fact
| mine) says you can't learn an API _unless_ you can see
| how it 's implemented. I don't think that's a remotely
| plausible reading of what was written. Instead I agree
| with the claim they actually wrote that since software is
| intended first and foremost to be read, it makes sense
| that the standard library, software you'll be using as a
| programmer should be a _positive example_ and not a
| horrible distorted mess.
| formerly_proven wrote:
| Part of the distinct "C++ std" style is due to naming rules
| and textual includes. Every name that's not part of the
| standardized interface starts with __ because __foo and _Foo
| are blanket reserved names, so a user can't complain that
| std:: explodes when he does "#define _Base 0".
| tialaramex wrote:
| Notice that there are in practice _three_ distinct
| implementations of the C++ standard library. They 're all
| awful to read though, here's Microsoft's std::vector
| https://github.com/microsoft/STL/blob/main/stl/inc/vector
|
| However you're being slightly unfair because Rust's Vec is
| just defined (opaquely) as a RawVec plus a length value, so
| let's link RawVec, https://doc.rust-
| lang.org/src/alloc/raw_vec.rs.html -- RawVec is the part
| responsible for the messy problem of how to actually
| implement the growable array type.
|
| Still, the existence of _three_ C++ libraries with slightly
| different (or sometimes hugely different) quality of
| implementation means good C++ code can 't depend on much
| beyond what the ISO document promises, and yet it must guard
| against the nonsense inflicted by all three and by lacks of
| the larger language. In particular everything must use the
| reserved prefix so that it's not smashed inadvertently by a
| macro, and lots of weird C++ idioms that preserve performance
| by sacrificing clarity of implementation are needed, even
| where you'd ordinarily sacrifice to get the development
| throughput win of everybody know what's going on. For example
| you'll see a lot of "pair" types bought into existence which
| are there to squirrel away a ZST that in C++ can't exist,
| using the Empty Base Optimisation. In Rust the language has
| ZSTs so they can just write what they meant.
| clnhlzmn wrote:
| I think the c++ version could be more understandable but it's
| as if the authors intentionally made it as obtuse as
| possible.
| bregma wrote:
| The authors are required to make it obtuse. They're
| required to use warts on all of the names because most of
| the code is in the head files and is generative code
| compiled by users of the library rather than the vendor. In
| order to avoid naming conflicts they can only use obscured
| names in their implementation of any but the defined API
| (eg. naming any internal functions, macros, or variables
| with leading underscores).
|
| So, the authors did intentionally make it as obtuse as
| possible for your benefit. It's written to be used, not
| studied, by all kinds of developers in all kinds of
| circumstances.
| gosub100 wrote:
| They could supply a "pretty" version for people who want
| to review it. Every time I have to step through code (and
| accidentally step into STL code) it looks sloppy and
| gross, like a swamp. No comments or organization. I would
| expect something neatly formatted, and comments saying
| "This is overload-4 of std::copy()..." etc.
| corysama wrote:
| The evolution of C++ has been a multi-decade history of dealing
| with difficult reality.
|
| I have great hope that Herb can create with his cppfront
| project "The Very Best of C++" to carry that tremendous legacy
| forward.
|
| If I was to throw my hat into a "C++ successor", it would be
| https://www.hylo-lang.org/ with its "all the safeties" and
| "tell you when you're doing it sub-optimal" approach.
| tialaramex wrote:
| Hylo is interesting in principle for exploring this
| particular notion (mutable value semantics) as a way to
| potentially write software without the lifetime annotations
| Rust needs.
|
| But I don't find it promising that after apologising in 2023
| for missing their self-imposed 2022 deadline to ship
| something that works and other people can use, in Q2 2024 it
| doesn't look like their new 2023 roadmap got done either.
| Maybe they're going to eventually deliver this amazing thing.
| Maybe they're just going to learn some lessons (probably for
| the Swift community) and never ship Hylo per se. Certainly
| 2025 "Take over the world" looks... ambitious with nine
| months left to do all the stuff left from 2023 and all the
| work described for 2024 on top.
| germandiago wrote:
| For me Hylo is the most elegant object model I have seen so
| far in the sense that it achieves dafety without all the
| anniyations and borrow checks that Rust performs. It just
| fits well the model for thinking yet it enables lots of
| optimizations. Of particular mention is that copies are
| lazy and can be passed freely and will only be done
| transparently on demand. That is what keeps the model
| simple.
| dang wrote:
| Related:
|
| _Cppfront, Herb Sutter 's proposal for a new C++ syntax_ -
| https://news.ycombinator.com/item?id=32877814 - Sept 2022 (545
| comments)
|
| and also _Cppfront: Autumn Update_ -
| https://news.ycombinator.com/item?id=37719729 - Sept 2023 (8
| comments)
| jokoon wrote:
| Someone on reddit said that the cpp2 repo was a bit old, and that
| is true, although he may have started this as an experiment
| influenced by typescript and left it on the side at some times.
|
| Anyway I have no idea if cpp2 would get support from microsoft or
| other devs, but cpp2 seems like the most humble and "least risky"
| solution for the future of C++, and I really want it to be.
|
| What I remember the most that Herb Sutter said in his cpp2 talk,
| is that it aims to avoid 95% of bad coding practices that C++
| allows today.
|
| It's safe to say that beyond the valid criticism of C++, that it
| quite a good goal and it would improve C++, without using a new
| language, and that's good, because a new language causes
| problems: new toolchains, new semantics, new specifics, no
| experience on a new language.
|
| Cpp2 is not a new language, it is the same semantics of C++,
| except it has a new syntax and enforces good practices.
|
| One very interesting point: in the future, cpp2 allows a
| cpp2-only compiler to be born, and it would still live next to
| C++ binaries without problem. That cpp2 compiler might probably
| be much faster since the cpp2 is a smaller stricter subset.
| gumby wrote:
| > Anyway I have no idea if cpp2 would get support from
| microsoft or other devs...
|
| Doesn't he work for Microsoft?
| dgellow wrote:
| Yes but it's his own personal project
| petre wrote:
| > Anyway I have no idea if cpp2 would get support from
| microsoft or other devs, but cpp2 seems like the most humble
| and "least risky" solution for the future of C++, and I really
| want it to be.
|
| That ship has sailed. They already have C#.
| qalmakka wrote:
| To be fair C# is vastly different in terms of semantics
| compared to C++. There's a lot of areas where it's not viable
| to use C#, and IMHO it also has its own share of legacy and
| bad decisions from its "Java clone" days that make it
| impossible to prefer it to C++ sometimes.
|
| Btw Microsoft is definitely interested into adopting new
| languages, just look all the effort they've been pouring into
| Rust lately.
| pjmlp wrote:
| Rust is now the official system programming language for
| Azure infrastructure, alongside managed languages, and it
| appears there is a soft spot to use Go instead of C#, when
| looking at what Azure opens up.
|
| WinDev, Office and XBox is another matter, they are deep
| into C++ and COM culture.
| flohofwoe wrote:
| C# is so old by now, if it would be a good replacement for
| C++ at MS, it would already have replaced it.
| barrenko wrote:
| Java and C# are in their "let's get functional" phase.
| neonsunset wrote:
| :(
|
| C# has been at that for more than a decade and a half
| already. Java's arrival to the party is extremely late
| and still lacking in many areas.
| pjmlp wrote:
| C# could have been like D back in 2001, yet it had to
| wait to go open source, away from the wings from WinDev
| politics, having to provide a replacement to C++/CLI
| capabilities in a cross-platform way, to finally expose
| the full CLR to C#, without us having to routinely
| manually generate MSIL or reach out to C++/CLI.
|
| Singularity and Midori were hardly taken seriously in any
| form by WinDev.
| pjmlp wrote:
| Don't mix technology with the internal politics at
| Microsoft, where some business units won't use anything
| else besides COM and C++, no matter what.
|
| They are so strong that they were responsible pushing the
| whole company into the whole Windows 8 debacle with WinRT,
| where COM was supposed to finally replace .NET.
|
| https://arstechnica.com/features/2012/10/windows-8-and-
| winrt...
| maxloh wrote:
| How about Google's Carbon language?
|
| https://github.com/carbon-language/carbon-lang
| joshkel wrote:
| Carbon and Cpp2 are both "C++ successors," broadly speaking.
| However, they take different approaches in their design:
|
| Carbon is a brand-new language that's designed to have
| seamless interoperability to C++.
|
| Cpp2 _is_ C++, just with a different syntax. (The goal is to
| even be able to mix Cpp2 and C++ syntax in the same source
| file. The initial implementation transpiles Cpp2 to C++
| source code, similar to how the original C++ implementation
| transpiled C++ to C, rather than implementing a full
| compiler.)
|
| Their overall goals appear to be different, too: The Carbon
| README states, "Existing modern languages already provide an
| excellent developer experience: Go, Swift, Kotlin, Rust, and
| many more. Developers that can use one of these existing
| languages should." With that mindset, it sounds like more of
| a stopgap for projects already invested in C++. In contrast,
| I believe that Sutter would argue that C++ still has the
| potential to be a viable choice for many tasks, and Cpp2 is a
| way to realize that.
| kreco wrote:
| > // 'BufferSize' is an object defined as a synonym for the value
| 1'000'000
|
| > BufferSize: i32 == 1'000'000;
|
| So "value : i32 = 10" is variable, but "value : i32 == 10" is a
| constant.
|
| The difference is so subtle I'm not sure I like it.
|
| Later in the documentation you can find "equals: (a, b) a == b;"
| which is a function but it feels like I need to decipher it
| because "==" is not for alias in this case.
|
| Retaking the example of "equals: (a, b) a == b;" it feels also
| odd to omit the braces because they are even enforced for if/else
| branches.
|
| I have to admit that everything was interesting until the
| "Summary of function defaults" part.
| FpUser wrote:
| Unless I can step through original source code in debugger, watch
| variables etc. etc. I could not accept any source to source
| translator in my practice.
| nathanrf wrote:
| It is a good thing that cppfront lets you do that, then!
|
| Cppfront generates #line pragmas which tell the generated .cpp
| file which source lines to "blame" for each piece of generated
| code. This isn't something new and fancy for cppfront, it's a
| bog-standard pragma that your debugger already understands. So
| it will work the exact same as your current debugging workflow
| even if you mix cpp and cpp2 source files.
| wheybags wrote:
| I'm working on a hobby project language that generates plain
| C as output, and debugger integration has been one of my big
| worries. If that works, then this is awesome, thank you!
| typ wrote:
| Looks neat and convincing. But I think the goal (and effort) to
| make it into the C++ standard would hinder the momentum of
| adoption. I suspect it would have to go through a lot of politics
| and bikesheding before making any real-world impact. Imagine that
| if Typescript had insisted to get accepted into the Emca standard
| before widespread adoption and promotion by Microsoft; it would
| probably still stay in the 'experimental' stage.
| howtofly wrote:
| Any good CMake integration besides https://github.com/modern-
| cmake/cppfront?
| cassepipe wrote:
| xmake handles cppfront :
| https://xmake.io/#/guide/project_examples?id=cppfront-progra...
| grumpy_coder wrote:
| It's a nice idea, but not clear that it can truly interoperate
| with vanilla C++ libs which I think is required. Seems to be
| waiting for modules to be finalized, but whether cpp2 can call
| cpp, and cpp can call cpp2 without implementing half of a C++
| compiler isn't obvious.
| https://github.com/hsutter/cppfront/issues/594
| iamaredpanda wrote:
| It's a just a transpiler to valid C++ code, so calling C++ code
| from cpp2 should be fine, but calling cpp2 code from C++ is an
| issue. There is no implementing half of a C++ compiler because
| it just uses clang or something after it is done transpiling.
| carlsborg wrote:
| At this point the focus should be on making interop with python a
| first class feature.
| teo_zero wrote:
| I like the idea, although many choices are arguable. For example,
| having to introduce mandatory/prohibited white space around
| binary/postfix operators (like "&") completely spoils the goal of
| having a more rational syntax.
| dfgdfg34545456 wrote:
| Great new idea in cpp. Automated bounds checking in the hello
| world example sold me straight away. Try and do that as tersely
| Haskellers. I hope this project gets momentum.
| skywal_l wrote:
| In this kind of thread I always mentionned Circle[0] from Sean
| Baxter. It's worth a look.
|
| [0] https://www.circle-lang.org/
| mgaunard wrote:
| I don't want bounds-checking, so lost interest immediately.
|
| This is a step in the wrong direction.
| dale_glass wrote:
| There's barely a point in making a better C++ if you're not
| going to address one of the most obvious footguns.
| mgaunard wrote:
| The main iteration this cycle for the next C++ is contracts.
|
| Contracts are all about introducing undefined behaviour if
| you don't satisfy a precondition.
|
| In practice this improves software quality on many levels by
| clearly defining requirements on interface boundaries that
| would otherwise be implicit or just documented.
|
| Of course you can have special debug modes where you actually
| check that contracts are being satisfied.
| tialaramex wrote:
| > The main iteration this cycle for the next C++ is
| contracts.
|
| As ever the C++ train leaves on schedule with or without
| anything you suppose is "promised" for that standard
| revision. This has been the practice since 2011 and I don't
| expect it to stop unless ISO tells them "Enough" or the
| whole thing comes apart.
|
| > Contracts are all about introducing undefined behaviour
| if you don't satisfy a precondition.
|
| Nope. That's _explicitly_ not what the proposal sets out to
| do. It is likely that, as usual, WG21 will manage to take
| facilities intended to be safe, file them to a sharp edge
| and then slit their own throats, but P2900 in its current
| form doesn 't do so. Here's an item from the proposal's
| list of things they're explicitly _not_ proposing:
|
| "The ability to assume that an unchecked contract predicate
| would evaluate to true, and allow the compiler to optimize
| based on that assumption, i.e. the assume semantic"
|
| One of the three significant implementers, Microsoft,
| actually _strongly_ objects to any idea of introducing yet
| more Undefined Behaviour into a language that is distinctly
| underwater when it comes to provable correctness. Microsoft
| shut down previous proposals in this area, and while they
| might (wrongly IMO) be sold the compromise position that 's
| advocated by some WG21 members today (some UB in some
| contract checks), you're asking for a lot more.
| pjmlp wrote:
| With Azure now only considering C++ for existing
| projects, and new projects require explicit reasoning why
| C or C++ instead of Rust, I expect some internal pressure
| at MS to do the right thing.
| mgaunard wrote:
| I've been involved with the C++ standards committee for
| 12+ years and my experience is that it is largely
| political; championing a proposal is more of a demagogic
| endeavor than anything else. Influential players have
| been priming contracts (that have been in the works
| forever) as a core objective this cycle and giving it a
| lot of momentum for it to happen this time. Of course if
| people still can't be convinced it's good enough before
| the deadline, it won't make the cut, but it's on track.
|
| Now regarding undefined behaviour, it's always been the
| reason why the proposal isn't moving forward. Some people
| believe religiously undefined behaviour is evil and will
| oppose anything tied to it. The kind of clause you
| mentioned is just to appease them and attain a
| compromise.
|
| But ultimately, contracts and undefined behaviour
| (depending on the definition you go for) are intimately
| linked.
|
| Consider lower_bound. The behaviour is undefined if the
| input is not sorted. That's just the way it is.
|
| You can't magically make a program well-formed if you
| breach preconditions even if you say optimizers are not
| allowed to make additional assumptions.
| ibobev wrote:
| I do not understand why there is a push to change the C++ syntax.
| There are plenty of new natively compiled languages like D, Go,
| Rust, Zig, Nim, Odin, Crystal, and so on. If you do not like C++
| you always can use some of them instead. The problems that make
| C++ unsafe are semantic and not syntactic ones.
| pjmlp wrote:
| The whole story about what cpp2 tries to be, distancing itself
| from other C++ wannabe replacements is only due to be coming from
| WG21 chair, which naturally can't talk about a C++ replacement as
| per conflict of interests.
|
| Cpp2 isn't an alternative syntax to C++, as much as C++ and
| Objective-C aren't alternative syntaxes for C, even though they
| support a subset of it, and were born exactly the same way, code
| translators into C.
|
| C didn't evolve into them, they became their own ecosystem,
| tainted by the underlying C compatibility.
|
| The only alternative that is really a Typescript for C++, is
| Circle.
___________________________________________________________________
(page generated 2024-04-01 23:02 UTC)