It means concurrency and pattern matching is not really critical for the community as it is for other language communities. There is no insult, but generally speaking an average C++ developer has deeper knowledge than an average Java developer which essentially means C++ developer is somewhat comfortable with traditional concurrency toolkit like threads.
> C++ developer is somewhat comfortable with traditional concurrency toolkit like threads.
As somebody who has used threads a lot and written code in kernel mode etc. something about this scratches me the wrong way.
The traditional concurrency toolkit has some issues. Being experienced with them can make you somewhat jaded against them. In particular, the standard way to solve problems in that mode of thinking is to introduce locks, and experienced people know all the problems that locks have, and that the "solution" is harmful in the extreme.
So it is acceptable for such a programmer to want more options, standardized. I would even argue that this is more important than introducing std::mutex, std::thread, or std::atomic, because you can say that in the pre-C++11 era nonportable interfaces like pthreads or win32 were already working OK for people that chose to use them.
The C++ committee has been captured by a hostile force that tries to turn every C++ compiler into the slowest imaginable C++ abstract machine emulator.
Why would we want concurrency if we already have thread support and coroutines. Concurrency is a nice-to-have abstraction that is not without overhead and design tradeoffs.
Compile-time programming is a very powerful feature of C++ that does not have an equivalent in other systems languages. It is commonly used to effect several types of compile-time verification of code design and safety that would be difficult to do any other way, in addition to its more obvious use case for effecting relatively complex performance optimization algorithmically. The ability to algorithmically generate almost arbitrarily complex types during compilation that are fit to purpose is an extremely useful feature for fast, high-reliability code and used pervasively in modern C++ as the facility has become more expressive. Filling some of the last remaining holes in its expressiveness would be high-value novel capability. Think of it as a hardcore safety feature.
You can put me in the camp that adding more support for concurrency in the language beyond a feature like coroutines (in C++20) and the existing primitives is not that useful. It would have the same issue as STL containers, where there simply isn't enough control of the implementation details for them to be used for many applications C++ is used for, so most concurrency infrastructure will be purpose-built anyway.
Pattern matching functionality can already be effected in many cases, albeit quite ugly. This would be more syntactic sugar than new capability, so I can understand why it isn't prioritized as much.
Sort of. Rust procedural macros are powerful, but they’re basically glorified code generation: they take a list of tokens as input and return a list of tokens as output. They don’t have access to type information. For that you need to use a combination of the trait system and `const fn`, to which the C++ equivalent is templates and `constexpr`. But C++ `constexpr` is currently way ahead of Rust `const fn` in functionality. And while Rust’s trait system tends to be nicer than C++’s templates for most purposes, it’s not as good at performing arbitrary computations. Overall, when I use either language I find myself badly missing features from the other. I look forward to seeing both close the gap…
Systems programming language is only a useful term if you define what systems you are talking about. Are you talking about creating an OS, highly performance micro services, distributed systems, etc.? Common Lisp would likely be a poor choice for an OS but would be great for some of those other types of systems.
I would say that the common feature of a systems language is that it is explicitly designed to enable direct control of hardware resources and -- very importantly -- operation scheduling by the programmer. A point I think is often overlooked is that some software architectures cannot be implemented correctly without this level of control, and for some applications those architectures are the only competent choice. Using a systems language should be driven by the requirements of application with respect to software architecture, and most applications don't have that requirement.
Some applications that are above the OS and device drivers, such as database engines, are clearly in the domain of systems applications because it is not possible to create a competent implementation without the control a systems language provides. Even for relatively abstract high-level software like distributed systems, strict control of scheduling is required for consistently robust throughput and behavior. Some key algorithms only work in real systems if resource management and concurrent operation sequencing is inexpensively deterministic. For some other applications, the only real benefit of a systems language would be for performance engineering purposes, which may not be a practical priority.
The "managed language" litmus test is quite good in my opinion, and almost definitional.
Also Java (annotation processors) and Scala (macros with full access to type system). Java is considered good enough systems language for some definition of a system - there are successful high performance database engines, web servers, web frameworks, compilers, distributed computing engines written in Java.
Another interesting thing about this survey is that the categories don't have equal representation; so for example, "pattern matching" has two papers, one of which is extremely popular, but "compile time programming" has thirteen papers, with a few of them being significantly more popular than the others (one has only one vote, for example, but another is, in my mind, one of the most exciting proposals coming down the line for C++).
There's tons of reasons why this could be this way; and it shows why creating these kinds of things is just hard.
It also might be a result of the pattern matching proposal having only been introduced this year, and concurrency/coroutines having been a focus in C++20 already. Whereas compile-time programming has been discussed for a while now and is getting more well-baked, but still hasn't made it fully into the language yet and many (myself included) are eager to use it. See the revision history of P0707 for example.
I would love that feature, and the ability to have a function that takes name + value pairs from a struct. However I suppose once you go down that rabbit hole, you may as well have all the features of annotating members and accessing that data too
Is there a petition for making C++ less ugly? There should be.
It’s a Frankenstein’s monster made of parts from other Frankenstein’s monsters. If Lovecraft were alive today this would be his prime eldritch deity. An abomination of impossible geometries you could not make sense of even if your eyes could see in 4D.
Pattern matching is listed there, but I think the onus is on you to describe what you're talking about. I guarantee that any language (even the HN-beloved Rust and Go) can produce hideous code.
If there's a valid need for the distinction, and it's straightforward (<>s search system libs first vs quotes search CWD first IIRC), what would be less ugly? Keeping in mind the vast number of C++ files out there that have already baked this convention in :)
Backwards compatibility is one of the highest priorities for C++, which is a significant constraint in removing "ugly" constructs and syntax. It can also force the use of verbose or unusual syntax for new features, even if the new features are a net positive for readability.
The "compile-time programming" category of this survey is dominated by constexpr proposals, where constexpr can replace some pretty ugly template metaprogramming.
A lot of C++ changes are aimed at this. For example C++11 auto removed a lot of the ugliness, and is removing even more with auto template parameters and deduced return types.
There's already a solution for that: Rust. C++ lives and dies by it's commitment to keep all of its ugly. If you want all of the nice stuff and lessons learned without the cruft, choose Rust.
"Type Safety" is only at position #12. I would have expected that nowadays C++ programmers would have been a little more aware of the perils of a not-strong-enough type system.
C++ is already type-safe enough. The problem isn't a lack of language support, the problem is that programmers view safety as "needless boilerplate" and go to great lengths to subvert the language on safety issues.
Using libraries is such a pain in C++ compared to other languages that people choose worse libraries just because they come header-only. I don't understand why that is not a higher priority. Cargo is lightyears ahead of any C++ system.
Honest question because I don't know how cargo works.
How would a package management system for C++ work?
Would there be a centralized repository containing shared objects, static objects, and header files of all the packages? Who would pay for the hosting? How would you track which libraries are compatible with which compilers/OSes/architectures?
Seems like at this point it would be a herculean effort to create cpppm because of how fragmented the compilers/support is across systems. And so it would be a broken piece of garbage so people wouldn't use it.
A central repository is helpful but not strictly necessary. I just want to have a config file where I specify what version of a library I want and where the build system can fetch it and have that work recursively for dependencies.
> the build system can fetch it and have that work recursively for dependencies
Which build system? CMake? SCons? GNU Make? Unix Make? Autotools? Visual Studio Solution?
Cpp libraries are far from having a standardized build system. And without a standardized build system, you can't do what you describe.
I suppose you could say "CMake is the standard" and then only support CMake-compatible libraries, but again, that leaves hundreds of packages out of reach.
And why are these build systems necessary in the first place? They mostly exist to work around the lame library ecosystem, where you never know what's installed or where to find it. Solving the "library problem" would presumably render these tools and their horrible complexity obsolete.
If there were some type of system where library A says it depends on library B (via a URL or some other non-centralized thing), and the compiler knows how to resolve this, then you don't need 90% of the features these tools provide. You still need build parallelization and maybe detection for non-standard compiler features, but this is a much smaller and simpler problem.
> And why are these build systems necessary in the first place?
Because C/C++ compilers are fragmented. And because C/C++ is not very portable. If building for windows -> pass a bunch of -D flags to the compiler. If building for Linux, pass a different set of -D flags. Different compilers require different arguments.
> If there were some type of system where library A says it depends on library B ... and the compiler knows how to resolve this
Here's the crux of the problem. There is no organization that owns or maintains C++, unlike Rust. With Rust, there is only one compiler - rustc. With C++ there are at least 5 major ones maintained by 5 different (competing) organizations.
There's no way to do package management in C++ without massive build system rewrites. Consider:
I write a new C++ package called CoolCrypto. It depends on libssl. I wrote my package in CMake, libssl uses autotools. Barf, the package manager only speaks CMake and gives up.
> And why are these build systems necessary in the first place?
Because depending on the use case you generally need to build the same software in a lot of different configurations : debug / release runtimes on windows, various sanitizers, static / dynamic / dynamic with LTO, you also want to build some files only on some platforms, you want your users to be able with / without tests / examples / implementations of various types (e.g. enable / disable network protocols or media codecs at build time for the two most common use cases I know), enable / disable profiling or coverage or tracing support... add a flag for having split debug info but you need to disable it if your users run ubuntu 16.04 because here it's broken, add a flag for linking with lld but you need to disable it on macos because it's broken, change debug mode to -Og if you're on a CI machine because -O0 takes too much space and you fill the Travis CI vm, except if you're on a worker with a recent enough toolchain to support -Wl,--compress-debug-sections=zlib
or one of the project I work on - most of the config flags here were added after the request of some users who wanted to be able to make a leaner build in some specific case ; making multiple shared libraries is a no-go first because then your users complain that they don't which of the five libraries they have to add to their system, and also because you loose LTO and have a lot of duplication of template instantiations (or monomorphized generics as it is said in Rust parlance):
https://github.com/OSSIA/libossia/blob/master/cmake/OssiaOpt...
You rarely have complex problems with simple solutions.
> How would a package management system for C++ work?
> Would there be a centralized repository
> How would you track which libraries are compatible
> Seems like at this point it would be a herculean effort
This is not a bizarre or impossible idea.
Besides the plethora of package managers in more modern languages, there are in fact, actual real-world C/C++ package managers: Conan [1], Buckaroo [2], vcpkg [3], Conda [4], cget [5].
However, it's much like Go before its maintainers did anything: it's the Wild West and every day brings a new package manager.
Having some consensus, standardization, common concepts, or direction would help C++ adoption/usability enormously.
vcpkg seems to be an honest attempt by Microsoft. They've recently introduced Mac and Linux support. It suppose to replace the pkg-config backend for cmake's find_package() but I haven't yet managed to get it working across platforms.
Herb's error handling proposal is great. It's very similar to Swift's error handling – exception-like syntax, but with deterministic behavior. Glad to see it at #3 (with the caveat that it was obviously Herb's survey).
I unfortunately quite hate the direction of not being able to write conforming programs that handle out-of-memory in a way that's distinct from terminating. That's a great default for a large majority of systems, but e.g. for a database server it sucks. When the system is configured appropriately (which is not the default, and has its own sets of problems, in particular with fork()), postgres e.g. handles the vast majority of out-of-memory cases gracefully, by just failing the query that encountered the exhaustion.
With this proposal, that's effectively impossible to do, without either being non-conforming ("with the additional restriction that any installed new_handler must terminate"), or not using any standard library code (which is proposed to not throw any exceptions: "For each standard function for which allocation is the only reportable error, make the function noex-cept").
It's practically impossible to use only explicitly checked allocations everywhere. One can do it for allocations that are more likely to fail (e.g. because they're large), but not for every small thing. In postgres, it's my experience that a very significant fraction of real-world allocation failures are on small allocations, not huge ones.
Now postgres is currently C (but that's imo more historical baggage than anything, and has pretty significant costs), but this would effectively prevent us from taking advantage of a lot of C++ if we ever decided to move.
FWIW, postgres OOM handling basically works by doing allocations via it's 'memory contexts', which check allocation failure (obviously only really possible if the OS signals them, requiring e.g. to explicitly change the overcommit settings on linux). Allocation failures are handled by switching to a memory context that sigsetjmp's to a higher level. That higher level then releases resources by bulk-releasing memory using the memory context mechanism, and handles other resources (locks, other resources) via the related resource owner mechanism. But that mechanism is - as far as I know - impossible to do in realistic confirming C++, because one cannot call all the relevant destructors.
I'm not a C++ programmer but I noticed this conflict. C++ is adding features like coroutines that perform hidden allocations, while at the same time shifting away from exceptions. You can't have it both ways.
You can have hidden allocations, you just need an exception mechanism. It sounds like Postgres does this with setjmp/longjmp. That's typical. I do that with my JSON C library--https://github.com/wahern/json/blob/master/src/json.h#L72 And setjmp/longjmp is how Lua implements its exceptions.
Lua is interesting in that it's a dynamically-typed scripting language that permits recovery from memory allocation failures in a rigorous manner. It doesn't add much if any complexity to the Lua implementation--if anything the discipline arguably helps keep things simpler. But it does require the Lua VM authors to always be mindful about maintaining consistency of the VM state. In particular it means they have to make sure all dynamically allocated memory is properly anchored in the object graph. This type of discipline isn't something you can refactor for after the fact, but it's not as difficult as it seems if you start out this way (modulo the lack of compiler enforcement). The Lua C API is also carefully designed--most routines are implemented and documented to not do any hidden allocations. And unlike any other scripting language with a C API I've used or examined, Lua provides a plethora of convenient abstractions for keeping foreign data pointers anchored in the object graph, as well as the mechanisms and semantics that permit both C and Lua code to catch and reliably recovery from exceptions, including allocation exceptions.
By contrast, while Go conceptually could provide OOM recovery--it has an exception mechanism almost identical to Lua's, and likewise uses non-copying GC--but much of the implementation makes the assumption that allocation never fails. As a practical matter correct, reliable, and consistent recovery from OOM isn't possible for Go. As I said, it's not the type of thing you can add after the fact. So instead you see a recapitulation of decades of myths[1] and contrivances[2] focused on "managing" memory. I only pick on Go because massive concurrency, and in particular massively concurrent network sessions, is it's niche. Even at "web scale" where you have the same Go process running on dozens or hundreds of other machines, this neglect to tackle allocation failure has resulted in often times exceptionally poor quality of service for Go apps. Without proper allocation failure recovery, "managing" memory becomes a black art rather than a science, with predictable results in terms of consistency and stability.
It's a shame to see C++ gleefully going down this road, seemingly having finally drunk the Kool-Aid and adopted the myth that handling allocation failure to too difficult and too error prone; that it's not your problem and something else higher up the stack will solve the problem for you. :(
[1] E.g. big allocations are more likely to fail
[2] E.g. reserving large blocks to be released according to some imaginative heuristic intended to predict and forestall allocation failure
> It's a shame to see C++ gleefully going down this road, seemingly having finally drunk the Kool-Aid and adopted the myth that handling allocation failure to too difficult and too error prone; that it's not your problem and something else higher up the stack will solve the problem for you. :(
I think the reasoning behind this part of the C++ proposal at hand is a bit more complicated than that. It's imo worth reading section 4.3 specifically about the memory allocation issue, and the motivation for the whole exception overhaul in in section 2 (although that's a bit longer).
The reasoning isn't itself that it's too difficult or such, it's that the overhead required by something like the current exception handling is high, and that it's quite desirable to makr a lot more of the standard library as not throwing exceptions - and the only cause a lot of them throw, is OOM.
What's wrong with runtime reflection? Seems more flexible and generalizable than inventing hierarchies of metaclasses, some of which include reflection accessors.
57 comments
[ 2.9 ms ] story [ 118 ms ] threadAs somebody who has used threads a lot and written code in kernel mode etc. something about this scratches me the wrong way.
The traditional concurrency toolkit has some issues. Being experienced with them can make you somewhat jaded against them. In particular, the standard way to solve problems in that mode of thinking is to introduce locks, and experienced people know all the problems that locks have, and that the "solution" is harmful in the extreme.
So it is acceptable for such a programmer to want more options, standardized. I would even argue that this is more important than introducing std::mutex, std::thread, or std::atomic, because you can say that in the pre-C++11 era nonportable interfaces like pthreads or win32 were already working OK for people that chose to use them.
nowadays I mostly see lock-free queues and message passing across threads, it's trivial to do with a few nice libraries
Compile-time programming is a very powerful feature of C++ that does not have an equivalent in other systems languages. It is commonly used to effect several types of compile-time verification of code design and safety that would be difficult to do any other way, in addition to its more obvious use case for effecting relatively complex performance optimization algorithmically. The ability to algorithmically generate almost arbitrarily complex types during compilation that are fit to purpose is an extremely useful feature for fast, high-reliability code and used pervasively in modern C++ as the facility has become more expressive. Filling some of the last remaining holes in its expressiveness would be high-value novel capability. Think of it as a hardcore safety feature.
You can put me in the camp that adding more support for concurrency in the language beyond a feature like coroutines (in C++20) and the existing primitives is not that useful. It would have the same issue as STL containers, where there simply isn't enough control of the implementation details for them to be used for many applications C++ is used for, so most concurrency infrastructure will be purpose-built anyway.
Pattern matching functionality can already be effected in many cases, albeit quite ugly. This would be more syntactic sugar than new capability, so I can understand why it isn't prioritized as much.
Yes, C++ compile time capabilities are powerful, but they aren't by all means the only game in town.
Some applications that are above the OS and device drivers, such as database engines, are clearly in the domain of systems applications because it is not possible to create a competent implementation without the control a systems language provides. Even for relatively abstract high-level software like distributed systems, strict control of scheduling is required for consistently robust throughput and behavior. Some key algorithms only work in real systems if resource management and concurrent operation sequencing is inexpensively deterministic. For some other applications, the only real benefit of a systems language would be for performance engineering purposes, which may not be a practical priority.
The "managed language" litmus test is quite good in my opinion, and almost definitional.
There's tons of reasons why this could be this way; and it shows why creating these kinds of things is just hard.
Another bias all voluntary surveys are prone to is prone to that's not listed is https://www.quora.com/What-is-voluntary-response-sampling-Wh...
So basically, don't use this data to make decisions :)
I'm sick of the dumb preprocessor hacks required for something as simple as iterating through enum values with their string representations.
It’s a Frankenstein’s monster made of parts from other Frankenstein’s monsters. If Lovecraft were alive today this would be his prime eldritch deity. An abomination of impossible geometries you could not make sense of even if your eyes could see in 4D.
I'll start with a simple example. I haven't worked with C++ in ages and I hope never to, but in the last C++ file I came across:
Right at the beginning there were 3 different kinds of include/import statements.
One with "" and another with <> and I don't remember what the third was but it had yet another format.
Now, I know what they mean, or I can look it up, and there may be a valid need for the distinction, but it is ugly.
Here's another confusion I was recently reading about:
https://stackoverflow.com/questions/516237/when-should-i-use...
I find Assembly and "pure" C a lot more elegant than this nightmarish atrocity.
Specify specific paths by something like "$SYSTEM\foo.h" or "$CWD\foo.h"
Either would be more readable and less magic than <> or ""
How would a package management system for C++ work?
Would there be a centralized repository containing shared objects, static objects, and header files of all the packages? Who would pay for the hosting? How would you track which libraries are compatible with which compilers/OSes/architectures?
Seems like at this point it would be a herculean effort to create cpppm because of how fragmented the compilers/support is across systems. And so it would be a broken piece of garbage so people wouldn't use it.
Which build system? CMake? SCons? GNU Make? Unix Make? Autotools? Visual Studio Solution?
Cpp libraries are far from having a standardized build system. And without a standardized build system, you can't do what you describe.
I suppose you could say "CMake is the standard" and then only support CMake-compatible libraries, but again, that leaves hundreds of packages out of reach.
If there were some type of system where library A says it depends on library B (via a URL or some other non-centralized thing), and the compiler knows how to resolve this, then you don't need 90% of the features these tools provide. You still need build parallelization and maybe detection for non-standard compiler features, but this is a much smaller and simpler problem.
Because C/C++ compilers are fragmented. And because C/C++ is not very portable. If building for windows -> pass a bunch of -D flags to the compiler. If building for Linux, pass a different set of -D flags. Different compilers require different arguments.
> If there were some type of system where library A says it depends on library B ... and the compiler knows how to resolve this
Here's the crux of the problem. There is no organization that owns or maintains C++, unlike Rust. With Rust, there is only one compiler - rustc. With C++ there are at least 5 major ones maintained by 5 different (competing) organizations.
There's no way to do package management in C++ without massive build system rewrites. Consider:
I write a new C++ package called CoolCrypto. It depends on libssl. I wrote my package in CMake, libssl uses autotools. Barf, the package manager only speaks CMake and gives up.
Because depending on the use case you generally need to build the same software in a lot of different configurations : debug / release runtimes on windows, various sanitizers, static / dynamic / dynamic with LTO, you also want to build some files only on some platforms, you want your users to be able with / without tests / examples / implementations of various types (e.g. enable / disable network protocols or media codecs at build time for the two most common use cases I know), enable / disable profiling or coverage or tracing support... add a flag for having split debug info but you need to disable it if your users run ubuntu 16.04 because here it's broken, add a flag for linking with lld but you need to disable it on macos because it's broken, change debug mode to -Og if you're on a CI machine because -O0 takes too much space and you fill the Travis CI vm, except if you're on a worker with a recent enough toolchain to support -Wl,--compress-debug-sections=zlib
e.g. look at the configurability of something like ffmpeg : https://paste.ofcode.org/zBhsKWiSk3Gn73bkgfA9Yk
or qt : https://paste.ofcode.org/wmZZX9emiNr2uPksceZNz6
or one of the project I work on - most of the config flags here were added after the request of some users who wanted to be able to make a leaner build in some specific case ; making multiple shared libraries is a no-go first because then your users complain that they don't which of the five libraries they have to add to their system, and also because you loose LTO and have a lot of duplication of template instantiations (or monomorphized generics as it is said in Rust parlance): https://github.com/OSSIA/libossia/blob/master/cmake/OssiaOpt...
You rarely have complex problems with simple solutions.
> Would there be a centralized repository
> How would you track which libraries are compatible
> Seems like at this point it would be a herculean effort
This is not a bizarre or impossible idea.
Besides the plethora of package managers in more modern languages, there are in fact, actual real-world C/C++ package managers: Conan [1], Buckaroo [2], vcpkg [3], Conda [4], cget [5].
However, it's much like Go before its maintainers did anything: it's the Wild West and every day brings a new package manager.
Having some consensus, standardization, common concepts, or direction would help C++ adoption/usability enormously.
[1] https://conan.io/
[2] https://buckaroo.pm/
[3] https://docs.microsoft.com/en-us/cpp/build/vcpkg?view=vs-201...
[4] https://conda.io/en/latest/
[5] https://cget.readthedocs.io/en/latest/
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p070...
With this proposal, that's effectively impossible to do, without either being non-conforming ("with the additional restriction that any installed new_handler must terminate"), or not using any standard library code (which is proposed to not throw any exceptions: "For each standard function for which allocation is the only reportable error, make the function noex-cept").
It's practically impossible to use only explicitly checked allocations everywhere. One can do it for allocations that are more likely to fail (e.g. because they're large), but not for every small thing. In postgres, it's my experience that a very significant fraction of real-world allocation failures are on small allocations, not huge ones.
Now postgres is currently C (but that's imo more historical baggage than anything, and has pretty significant costs), but this would effectively prevent us from taking advantage of a lot of C++ if we ever decided to move.
FWIW, postgres OOM handling basically works by doing allocations via it's 'memory contexts', which check allocation failure (obviously only really possible if the OS signals them, requiring e.g. to explicitly change the overcommit settings on linux). Allocation failures are handled by switching to a memory context that sigsetjmp's to a higher level. That higher level then releases resources by bulk-releasing memory using the memory context mechanism, and handles other resources (locks, other resources) via the related resource owner mechanism. But that mechanism is - as far as I know - impossible to do in realistic confirming C++, because one cannot call all the relevant destructors.
You can have hidden allocations, you just need an exception mechanism. It sounds like Postgres does this with setjmp/longjmp. That's typical. I do that with my JSON C library--https://github.com/wahern/json/blob/master/src/json.h#L72 And setjmp/longjmp is how Lua implements its exceptions.
Lua is interesting in that it's a dynamically-typed scripting language that permits recovery from memory allocation failures in a rigorous manner. It doesn't add much if any complexity to the Lua implementation--if anything the discipline arguably helps keep things simpler. But it does require the Lua VM authors to always be mindful about maintaining consistency of the VM state. In particular it means they have to make sure all dynamically allocated memory is properly anchored in the object graph. This type of discipline isn't something you can refactor for after the fact, but it's not as difficult as it seems if you start out this way (modulo the lack of compiler enforcement). The Lua C API is also carefully designed--most routines are implemented and documented to not do any hidden allocations. And unlike any other scripting language with a C API I've used or examined, Lua provides a plethora of convenient abstractions for keeping foreign data pointers anchored in the object graph, as well as the mechanisms and semantics that permit both C and Lua code to catch and reliably recovery from exceptions, including allocation exceptions.
By contrast, while Go conceptually could provide OOM recovery--it has an exception mechanism almost identical to Lua's, and likewise uses non-copying GC--but much of the implementation makes the assumption that allocation never fails. As a practical matter correct, reliable, and consistent recovery from OOM isn't possible for Go. As I said, it's not the type of thing you can add after the fact. So instead you see a recapitulation of decades of myths[1] and contrivances[2] focused on "managing" memory. I only pick on Go because massive concurrency, and in particular massively concurrent network sessions, is it's niche. Even at "web scale" where you have the same Go process running on dozens or hundreds of other machines, this neglect to tackle allocation failure has resulted in often times exceptionally poor quality of service for Go apps. Without proper allocation failure recovery, "managing" memory becomes a black art rather than a science, with predictable results in terms of consistency and stability.
It's a shame to see C++ gleefully going down this road, seemingly having finally drunk the Kool-Aid and adopted the myth that handling allocation failure to too difficult and too error prone; that it's not your problem and something else higher up the stack will solve the problem for you. :(
[1] E.g. big allocations are more likely to fail
[2] E.g. reserving large blocks to be released according to some imaginative heuristic intended to predict and forestall allocation failure
I think the reasoning behind this part of the C++ proposal at hand is a bit more complicated than that. It's imo worth reading section 4.3 specifically about the memory allocation issue, and the motivation for the whole exception overhaul in in section 2 (although that's a bit longer).
The reasoning isn't itself that it's too difficult or such, it's that the overhead required by something like the current exception handling is high, and that it's quite desirable to makr a lot more of the standard library as not throwing exceptions - and the only cause a lot of them throw, is OOM.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p070...
It's also a painful topic, considering how vastly outdated Qt's use of C++ is, and how many libraries are doing similar.
Poor 'Copper Spice'!