Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Then, one day, decades and several versions hence, you may find yourself now dealing with all of the warts that language has accrued over time.

What languages are getting even close to being as bad as C++ in this respect though? Java, Python and JavaScript have been around for a while now for example and they seem more opinionated in a way that stops too many warts from appearing.

C++ has undefined and compiler dependent behaviour in the core of its design which I think is the real problem. Its complexity just multiplies at an exponential rate as more features and undefined behaviour is introduced.



I would argue that there are rabbit holes in Haskell that can lead to baroque terrors of syntax on par with the C++ horrors in the article. The difference between Haskell and C++ is that Haskell code always has a well defined meaning even if it is _insanely_ terse. There is no ambiguity like with C++.


C# maybe? The amount of features in it is very rapidly nearing those of C++.


Javascript certainly has. Most people don't even seem to be writing javascript in javascript anymore.


Most languages have Undefined and Implementation specific behavior. Few languages have so carefully specified their undefined behavior as C++ though.


Undefined behavior is not specified, by definition! It is the absence of requirements, including the requirements to diagnose.

C++ has something called "unspecified behavior" also, which is stronger than undefined, because it gives requirements. It denotes some circumstances when an implementation can behave in one of a finite possible ways (none of which fail) and must choose one of them without documenting which choice is made. For instance the expression f(a(), b(), c()) can call the functions a, b and c in any of the six possible permutations of the order: no particular order is specified. (This arises due to the order of evaluation of function arguments being unspecified, together with the sequenced evaluation of function calls.)

The specifications of C and C++ contain large number of places in which they carefully state that no requirements apply to some situation. This is not a good thing; it's basically a myriad holes punched in the requirement spec, any one of which could be a pitfall.

Few languages are so chock full of holes in their requirements.


> Undefined behavior is not specified

But it is precisely specified how to invoke undefined behavior. For example when to numbers are added and they overflow the size of the type. This is carefully specified when this is or isn't undefined behavior. The C++ standard goes into excruciating detail but they have not covered everything

Compare that to many of the newer scripting languages. Elsewhere on this page I described how to recover from a segfault by setting string conversion function, which I don't think was specified anywhere. You can break some newer language by overriding innocuous function like #hash on some objects. These languages have at least as many pitfalls as C++ they are just less well described.


> Undefined behavior is not specified

> unspecified is stronger than undefined because it gives requirements.

I do not understand what you are saying.


"unspecified behavior" in C++ is a formal term denoting a situation in which requirements are in fact given. There are multiple possible requirements, and the implementation chooses which apply, without having to document the choice. So in fact something is specified, just not entirely.

"undefined behavior" refers to situations in which no requirements apply at all. (Truly unspecified in the regular sense of the word.)


Thanks, I understand your usage now.

Unspecified means the implementation has some leeway.

Undefined means the implementation can do anything.


Yes; as in "unspecified which choice".


Any examples? My experience is you will frequently be bitten by undefined behaviour in C++ if you don't know the rules well but this is very rare in other languages. It's rare you're going to get a segfault or the whole program crashing in languages like Python, Java and JavaScript.


Those languages all have heavy runtime environments; if a segfault occurs it's an interpreter bug. Whether the mistakes you can make are a result of "undefined behavior" is a different question whether from the consequences of a mistake are an abrupt termination, or an abrupt termination with a helpful error message.


This is how it works in Ruby.

In the CRuby/MRI interpretter when a segfault happens it calls the #to_s method on the current execution context, to get the some memory address and technical details. Ruby happens to be flexible enough that you can override that method. Then you can call whatever you want and you have effectly "recovered" from a segfault. The parser is totally in an inconsistent state and could crash at any moment for any reason but it works often enough people have done it live in front of audiences for talks.

It is not officially specified that I am aware of that overloading #to_s on anything allows recovering of segfaults it is intended for converting things to_strings.

For more on what I mean start reading the Ruby standard, which was about 1,500 pages last I checked. Then check the C++ standard which is about 3,000 for C++11 and then another 2,500 for the standard library. C++ with its standard library is much smaller than Ruby (in terms of the functionality provided) but has at least 3 times the specificity.


You can do the same thing in C++, either by intercepting signals on Linux, or setting up a structured exception handler on Windows. It's useful when you either A) want to attempt a recovery or B) have a complex tear down.


But in C++ you are specifying a signal handler, not overloading a string conversion function.

Imagine your surprise when you discover via dmesg that your have generates hundreds of segfault per second but a string conversion functions hid this bug.


Interesting post. Small comment: the C++11 standard has about 1300 pages, not 5500, thank goodness.


I misremembered...

So I just went and look this up the C++14 standard, or at least the nNvember draft is 1368 pages and the Ruby ISO standard is 313. I was off by a fair amount.

But this made my point more extreme. C++ has mauch smaller feature list than Ruby and the spec is more 4x the size C++ is much more specific and precise. Ruby and many other languages will use the behavior of some de-facto implementation as their standard.


Is C++ really better specified? Some languages use "the implementation is the spec" and that's bad, sure (though I'd argue it's less bad than "programs that worked on previous versions of the compiler will contain memory safety vulnerabilities on newer versions of the compiler", which is the end result of C++'s approach to undefined behaviour). But compare to e.g. Java, which has a (shorter!) spec with multiple implementations, including a well-specified memory model in the presence of threads (which weren't even specified at all by C++ until recently), and more importantly actually defines behaviour rather than specifying very carefully the wide range of conditions in which the compiler is permitted to arbitrarily break your code.


Memory is specifically hard one for C++ because it tries not to put a layer between the coder and the underlying hardware. Much of the memory model must be implementation defined.

It wasn't that long ago that we had different pointer for small fast memory and large slow memory, back when more that 64k was a real big deal. Kind of like how now video memory is a real big deal. Now we have a bunch of weird C APIs for putting things there (I presume these can be accessed from JNI or something similar), but these are temporary ugly hacks.

When video ram becomes a more common thing to refer to specifically, what will that look like in Java? That weird memory that C++ has is ready, because it has done exactly this in the past a few times. I am also looking forward to C++17 and its newer way to run std algorithms on heterogeneous executors (like GPUs).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: