> This propensity for today’s working programs to be broken tomorrow is what I mean when I say these languages are not future proof.
It doesn't matter. This is not how programming works in the real world. In the real world, you write the most correct program you can under time pressure. A new compiler, operating system, or platform arrives that exposes a bug. You fix it and you move on. It doesn't matter if the language is future proof or not. The process is similar for any complex program.
The blog's name is "Embedded in Academia" and this is perfectly valid viewpoint for someone in academia to take. And people in academia should research towards building more robust tools and languages. But it really is not going to matter in the real world. Languages and platforms will always not be future proof because computing is complex.
The particular kind of not-future-proofness he has in mind seems pretty practically important: code that relies on this undefined behavior often suffers from exploitable security holes. Just because computing is complex doesn't mean you have a free pass if you shoot yourself (or your customers) in the foot the same way the previous 100 folks did. If it happens enough, it becomes prudent to do something about it, like people finally did about unsanitized format strings, or the use of unbounded sprintf().
His suggestion #3, that the standards should define more of the commonly used behavior and leave less of it undefined, wouldn't even require C programmers to do anything about it themselves.
> His suggestion #3, that the standards should define more of the commonly used behavior and leave less of it undefined, wouldn't even require C programmers to do anything about it themselves.
I've written Windows, Mac, Linux, Xbox, PlayStation, PSP, iOS, and Android code. The memory model is subtly different for each platform. I just don't think you can define certain behaviour and have that work across disparate platforms.
I haven't really written any device drivers or kernel space code but I would imagine it would make the job even more difficult.
You underestimate how much undefined behaviour is in typical C programs and how little of it is yet taken advantage of by compilers.
The compilers are now starting to fairly radically rewrite the original code in ways the author would not recognize, simply because of some undefined behaviour exists within the code. You need to be increasingly language lawyerly to avoid the compiler outsmarting you, almost as if it was a hostile opponent.
The read of an uninitialized variable in the article was a good example.
The problem is that programmers have a mental model of how the C they write turns into machine code, and that model is increasingly out of date in the search for more performance. The compiler is becoming less predictable, in precisely the way that we argue against "sufficiently smart compilers" in the past for languages at a higher level than C - that you wouldn't be able to predict when the smart compiler was smart enough to optimize your high-level construct. Now you're increasingly unable to predict what the compiler will turn your code into, unless you have a deeper understanding of the rules.
The "hostile opponent" analogy is a good one. C was always intended as a kind of higher-level replacement for assembly, so it was reasonable to assume (for instance) that uninitialized integer variables contain some unspecified but definite value, but recently compilers have been deliberately breaking those assumptions just because they can. It's almost reached the point where C isn't useful for its original purpose of systems programming; it's very hard to write threaded code that doesn't rely on undefined behaviour, for instance.
Ostensibly, a platform like Java or Rust is supposed to abstract stuff like the memory model. I haven't written a lot of Java code, especially not Java code that runs on many different native system / VMs, but from my perspective of blissful ignorance, it seems to have done the job?
Same with other high-level VM based languages like Python...
For most programs, yes they have done their job. However, in certaint categories of applications (for example, server software) it's somewhat of a leaky abstraction. Garbage collector sweeps, circular references, etc are all pains which force you to be aware of how the vm is managing your memory.
It works, but to get C to expose the same memory model on different platforms you would have to compromise the performance and close-to-the-hardware nature that are the only reason to use C nowadays.
Small amounts of undefined behaviour are normal in most language specs though to give implementations flexibility. Tests to make sure you do not rely on them would be useful though.
The guy behind "Embedded in Academia" knows quite a bit about the memory models supported by C, and had done some marvelous work regarding the testing of C compilers, C code and undefined behavior. If he claims it is possible to improve the situation and leave less behavior undefined, he's most probably right.
You certainly could define some basic things to make the language safer. For example, make variables always be initialized to zero if not explicitly initialized, and force accessing beyond the bounds of an array to be a fault rather than undefined behavior.
You could, but that comes at a cost. That's why libraries like the STL in C++ provide std::vector::operator[] and std::vector::at() - so the user can freely choose whether to pay the extra cost for the bounds check, or not. That's why C provides both malloc() and calloc() - so the user can freely choose whether memory is zero-initialized, or not.
One of the major design decisions for C/C++ is that you don't pay for what you don't use. This is what makes them so flexible and performant across a wide range of systems and applications, but also leaves these safety choices up to the user. Some languages make that tradeoff, but it's not always the right decision.
On the other hand there are languages where correctness comes before speed, and they still provide you the mechanisms to get speed if you really want.
For example, in the Pascal family of languages, you can always disable bounds checking or do pointer arithmetic if you really want to, but that should only be done if there is really the need to do so.
A problem with many C and C++ developers is that they suffer from premature optimization, thinking that we are still targeting PDP-11 like environments.
Initializing variables to zero doesn't buy you much in terms of safety, IMHO. The value 0 isn't necessarily any more valid than an arbitrary value. Better is Java/ML/Haskell's rule whereby variables must be explicitly initialized before use. This can be implemented with a simple compiler pass.
At least the value 0 is always the same and doesn't subtly change from one invocation to the next or from one machine to the next. It certainly helps in making programs more robust, even if there is still a problem at code level.
I agree that programmers should not take on the burden of supporting hypothetical future compiler optimizations (if that's what you're saying), but this problem could be reduced if compilers started forbidding undefined behavior — then programmers would only have to adapt once.
Is there a way to detect it dynamically, e.g. by running C code under a debug mode or in an interpreter that errors out when undefined behavior is encountered? I've occasionally wanted to have something like that to use in tests, so I could ensure that at least my common code paths aren't relying on undefined behavior. I know about gcc's -ftrapv and a few other options, but nothing comprehensive.
- KCC : high overhead, for all kinds of undefined behavior, limited standard library support (and source-level only)
- Valgrind : medium overhead, for various memory errors, binary, may fail to detect undefined behaviors that have been made undetectable by compilation.
You may also find:
- various memory-safe C compilers. There are plenty here, I had better let you do the googling. medium overhead, generally better than Valgrind at being sound (since they work at source level), unless they trade efficiency for soundness: http://research.microsoft.com/pubs/101450/baggy-usenix2009.p... . May require all source code to be available.
- Frama-C's value analysis, a static analyzer that can be used as a C interpreter. This is what I work on. Limitations comparable to KCC, quite a bit faster (but still high overhead), some slightly different design choices. I do not have a good single write-up for this use, but some details are available at these URLs:
In theory, for sure. Valgrind can test for certain kinds of undefined behaviour - it runs the code in a special virtual machine.
You could also have the compiler insert checks. Obviously this isn't desirable for a lot of C projects by default, but (other than in places like kernel development etc.) it could be a nice debugging aid. I don't know of any good tools for doing this comprehensively.
But the the author of the original piece is mostly concerned with undefined behavior that can be detected statically - otherwise, compilers would not be able to exploit it to make optimizations.
One thing he mentions is signed integer overflow. This is in the worst case equivalent to the halting problem, but even in practice very hard to test for at compile time.
Another behaviour he mentions is not properly return'ing at the end of a non-void function. This is again technically equivalent to the halting problem, but it is negated by the good practice of making every code path (even potentially dead ones) have a return statement (or throw an exception, etc.) Go takes this approach if I remember correctly.
It can't always be tested for at compiler time but the problem he's complaining about is when C compilers do detect signed integer overflow. What happens is that someone writes code that in practice handles signed integer overflow fine, then a while later the C compiler developers get clever, detect the integer overflow, and decide to optimize that code away because it's invoking undefined behaviour and they can do whatever they like. The code in question is frequently security-critical, so by removing it the compiler converts safe code whose behaviour is technically undefined by the standard into a security vulnerability.
The common case is (probably) not that a compiler detects an instance of signed over/underflow. Instead, it can assume that this never happens and generate "dangerous" code.
For most large projects, you usually standardise the compilation environment for a specific release. Any issues for a newer version would be fixed when you make a newer release of your software. Especially for anything that is safety critical, like satellites or spaceships.
The software world does not solely consist of large, safety-critical projects.
Picture a single person or small team releasing an open-source project, it generates little developer interest and a community fails to start, and the original author(s) move on.
Fast forward 5 years or more. The code's floating around the internet, but nobody's left who understands it well enough to explain why it breaks with a modern toolchain. Requiring people to use a compiler -- and possibly an entire operating system -- of that age will deter people significantly from using that project.
In the real world, you write the most correct program you can under time pressure. A new compiler, operating system, or platform arrives that exposes a bug. You fix it and you move on. It doesn't matter if the language is future proof or not.
A new compiler, OS or platform will require much less rewriting of a Python program than a C program. Under time pressure, it is much more likely that you will incidentally write future-proof code if you write in Python instead of C.
I think there is an important point here, which is that C and C++ compilers have let us get away with a lot of undefined behavior for a long time, and that there hasn't been a lot of tooling to help avoid it nor a culture that stresses the long-term danger of depending on it.
I can speak as someone who has been programming in C and C++ for over ten years, but only in the last few years became aware of this issue and started taking it seriously. Five years ago I would do things like cast function pointers to void-pointer and back, or calculate addresses that were outside the bounds of any allocated object and compare against them, all without really even realizing I was doing something wrong.
I don't think this will spell doom-and-gloom for C and C++ though. I think a few things will happen.
First of all, the compiler people are walking a fine line; yes, they are breaking code that relies on undefined behavior, but they often avoid breaking too much. For example, I've had it explained to me that at least for the time being, gcc's LTO avoids breaking any programs that would work when compiled with a traditional linker. In addition, they often provide switches that preserve traditional semantics for non-compliant code that needs it (like -fno-strict-aliasing and -fwrapv).
Secondly, I believe that tooling will get better, and rather than ignoring the warnings I believe that people's general awareness of this issue will raise, as well as knowledge of standard-compliant ways of working around common patterns of undefined behavior. For example, it's often easy to avoid aliasing problems by using memcpy(), and this can usually be optimized away.
Thirdly, I expect that the standard may begin to define some of this behavior. For example, I think that non-twos-complement systems are exceedingly rare these days; I wouldn't be surprised if a future version of the standard defines unsigned->signed conversions accordingly.
I agree with all your arguments. The function pointer <-> void pointer conversion in particular is an excellent example. But the very last example, unsigned -> signed conversion, is not a good illustration of the point you are making then.
unsigned -> signed conversion is already “implementation-defined behavior” (as opposed to “undefined behavior”). The standard does not guarantee how it behaves but forces compilers to make a choice and to stick to it.
A different example, of a behavior that is really undefined, would be signed arithmetic overflow:
int detect_max(int x)
{
return x+1 < x;
}
The function above branchlessly detects that its argument is INT_MAX, and returns 1 in this case thanks to 2's complement representation.
Except that it doesn't. The command “gcc -O2” compiles it into “return 0;”. GCC can do this, because signed arithmetic overflow is undefined behavior. The compiler is only taking advantage of undefined behavior in a way locally convenient.
Now that two's complement is (almost) everywhere, making it the standard for signed arithmetic overflows is the sort of bold choice I would like to see, but it won't happen (it would break GCC's existing optimization).
Good point about undefined vs. implementation-defined, though even implementation-defined behavior could break programs that switch to a different implementation that makes a different choice.
Use cpplint, -Werror -Wall and use the Clang compiler toolchain. It has the best compiler time warnings out there. At the end of the day, the toolchains are getting better at error deduction. This problem will not be solved at the toolchain or language level, but patches for specific toolchains have always been the norm for any portable C++ or C library. C+11 removes dependency on poorly written libraries as well by moving many things to the std library.
This caught my eye "Program analyzers that warn about these problems are likely to lose users."
For me, this is perhaps the biggest issue raised in this article, as static and dynamic analysis tools become more ubiquitous we should be learning to fix the issues that they raise, not ignore them.
I remember a while ago (2004 or 5) interviewing a college-hire candidate, I had asked about working with others and we had gotten to talking about code review - the candidate was passionate about how code review had helped with a group project he worked on, but every single example he gave of a a bug found by code review was something that -Wall would have found...
The same applies to static analysis - let the machines do the work that they can do, that leaves the humans to get on with the work that the machines can't do (yet!)
The problem with smart compilers is indeed how they break existing (naive) code, optimizing away things like "assert(len + 100 > len)" [1]
Making a correct overflow check in C/C++ is not just not straightforward, it is overy complicated even for experienced developers [2]. This is IMHO inacceptable for a thing that is required often in a security context.
Therefore, I hope that option 3 proposed by the author (change of the C/C++ standard to define the correct behavior at for least integer overflows) will be adopted. However, this probably will not happen for a long time, leaving us with security holes all over the net.
C and C++ indeed aren't future-proof, but it's not juste because of undefined behavior, it's by remaining stuck firmly in the 1960's in terms of programming style.
C++11 added many changes intended for "do-it-yourself" crowd, like auto, new function syntax, lambdas. It didn't add much in terms of "let the compiler do the work for me" crowd (one notable exception being variadic templates, something that was in my own XL programming language since 2000). In C++, you are still supposed to do the boring work yourself.
For example, C++11 still lack anything that would let you build solid reflexion and introspection, or write a good garbage collector that doesn't need to scan tons of non-pointers.
If you want to extend C++, it's just too hard. C++11 managed to add complexity to the most inanely complex syntax of all modern programming languages. Building any useful extension on top of C++, like Qt's slots and signals, is exceedingly difficult. By contrast, Lisp has practically no syntactic construct and is future proof. My own XL has exactly 8 syntactic elements in the parse tree.
So in my opinion, C and C++ are already left behind for a lot of application development these days because they lack a built-in way to evolve. If you are curious, this is a topic I explore more in depth under the "Concept programming" moniker, e.g. http://xlr.sourceforge.net/Concept%20Programming%20Presentat....
So far I doubt C++ is going anywhere - it's here to stay. When the usage of such languages as Rust will gain more traction up to the point that high performance games engines will be written in it, one could start saying that C++ is being pushed out. But it's really somewhere in the future.
Hey, don't lump C++ in with this. If you write code in the STL weenie style or the Pretend It's Java style there aren't any idioms I know of that would ever violate the rules he mentions (out-of-range pointers, signed overflow, invalid aliasing). I don't do those things and the C++ programmers I work with don't do those things, at least not habitually. I don't see violations of undefined behavior rules, or the use of idioms that come close to it, very often in our code. Not nearly as often as the sort of mundane errors that no language can prevent.
These are not problems of a language per se, but the original sins of neo-vaxocentrism and confusing "I understand how this might work, at some random abstraction layer" and "I can depend on what happens when I do something stupid". Free your mind of these and the rest will follow.
These low-level bit banging errors are vastly less common than shared-memory concurrency issues, which as far as I can tell are endemic to all code that attempts shared-memory concurrency, in any language. If you want to have an axe to grind about languages that aren't future proof, look there.
"If you write code in the STL weenie style or the Pretend It's Java style there aren't any idioms I know of that would ever violate the rules he mentions (out-of-range pointers, signed overflow, invalid aliasing)."
What does the STL do about signed overflow? As for out of range pointers, that is an easy one to get with the STL:
vector<int> somevector(100);
somevector[200] = 5;
"These are not problems of a language per se"
Yes they are: the default numeric type is fixed-width, pointers pop up all over the place and pointer dereferences are unchecked by default. Personally, though, I would have chosen (as the article's author did) the more severe deficiencies in the standard, like the lack of any requirement that a function with a non-void return type have a return statement along every control path or the fact that there is no reliable way to signal errors that occur in destructors.
"These low-level bit banging errors are vastly less common"
Not in my experience, and not judging by the number of bug reports and vulnerabilities I have seen that stem from low-level mechanics.
I'm not saying the language is some sort of security barrier that prevents any error, I'm saying sanely styled code does not have these issues in practice. The solution is "don't do that, and cultivate habits that will not cause you to do that by accident", not having the compiler make up semantics for broken code or putting in checks everywhere. Just because someone, somewhere does it wrong, doesn't mean it's impossible to do it right.
this:
vector<int> somevector(100);
somevector[200] = 5;
Is a C idiom translated by cut-and-paste. The unmotivated poking of arbitrary magic-number offsets into a magic-number sized vector is not proper. It's the kind of thing that sets off alarm bells on even the most casual of review.
"I'm saying sanely styled code does not have these issues in practice"
Otherwise known as the "just do it right" argument. This is an argument that goes all the way back to the days of writing everything in assembly language, and it was just as wrong then as it is today. If only a restricted subset of a language can ensure that basic issues do not become serious problems, then the language should be restricted to that subset.
"not having the compiler make up semantics for broken code or putting in checks everywhere"
Really? I would rather have the compiler put in run time checks whenever it cannot infer that no input will cause the program's behavior to be undefined. Thus, the compiler might insert a check here:
for(i = 0; i < input.length(); i++)
some_vector[i]++;
but not here:
for(i = 0; i < min(input.length(), some_vector.length()); i++)
some_vector[i]++;
At the very least, requiring bounds checks on array access would create a definition for out-of-bounds pointers: program termination (or perhaps an exception being thrown). A reasonably good compiler can detect when a bounds check is unnecessary and can remote the bounds check as an optimization. Why shouldn't this be something that compilers do -- out-of-bounds array access is never a good thing (oh, wait, you might be dereferencing some arbitrary pointer that you got by some means other than allocating memory with "new" -- OK, fine, but that is what type systems are for; this sort of separation is not unheard of, I see it in Lisp with SBCL's FFI)?
"The unmotivated poking of arbitrary magic-number offsets into a magic-number sized vector is not proper. It's the kind of thing that sets off alarm bells on even the most casual of review."
Perhaps so, but then the answer is not simply "just use the STL." As with most things C++, it requires a long list of things to make code work right, and even people who have been writing C++ code for many years are sometimes surprised to discover that something they thought was fine is actually bad. C++ makes it pretty easy for programmers to do the wrong thing and needlessly difficult to do the right thing, which is why years of expertise are needed to write remotely reliable C++ code.
I really don't have any difficulty finding programmers who have the discipline to not use the unsafe parts of the language all over the place. C++ has an issue with having a fragmented multitude of sane subsets, but any of them are fine if they get the job done.
That said, I don't understand why you still keep putting up awful mostly-C code as if any trained C++ programmer wouldn't yell at you for doing it wrong, even before they saw the part with the error.
for(i = 0; i < input.length(); i++)
Where did you learn this? don't do this. Everyone else knows not to do this.
for(i = 0; i < min(input.length(), some_vector.length()); i++)
This is actually worse, though it does have the virtue of probably working. If you want a run-time check, use at(), or better still use an iterator already.
C++ has all sorts of issues. It's too hard to learn, it's missing some very useful features, and it has a number of rough edges that you have to learn your way around. But the things being complained about in the OP and by you are not real problems for anything but beginners. There just aren't that many naked array accesses or pointer math operations going on in an ordinary C++ application written in non-C style.
Iterators don't protect against iterator invalidation due to e.g. emptying a vector while you iterate over it. Accessing elements through an invalidated iterator is undefined behavior and can lead to exploitable security vulnerabilities.
Iterator invalidation is a giant hassle, although it's easier to catch with debug or safety-mode libraries. I kind of got dragged into a derail ranting about the bizarre strawmen above.
My core point is that the OP has a theory about there being a school of C programmers that intentionally or unintentionally invoke undefined behavior and expect the compiler to do the right thing. He's doing a pretty good job of backing it up, although I'm not sure I understand what exactly he's proposing to do about it.
... And then he just kind of throws C++ in for the ride, presumably on the argument that C++ is just like C with even more cases for undefined behavior. But that's not correct because he's making both a technical and a cultural argument. C++ is technologically (mostly) a superset of C, the culture is completely different to the extent that Linus famously argued that the main advantage of using C is that it keeps all the C++ programmers out. http://article.gmane.org/gmane.comp.version-control.git/5791...
Of the widely fragmented C++ user base, there are multiple, popular methods of development that encourage true high-level development were you are encouraged to target your code to the abstract/portable machine that the standard uses and not your personal guess of how the compiler should work and avoids doing things that require inordinate care to get right.
Again, C++ is full of practical problems, the kind of undefined behavior cases the OP worries about don't really rank up there among them.
You're arguing a straw man here. bcoates is saying, and I agree, that the usual examples being given on how horrible C++ is, are not idiomatic C++ and are used only by people who don't have any experience using C++. Of course it's easy to come up with examples of when things might go wrong. C++ is a powerful language, and with great power comes great responsibility, pardon the pompousness of that phrasing. C++ isn't perfect by a long shot, but the reasons brought forth in the OP and most of this discussion are not examples of real problems.
Many times it's the human error which causes the bug/vulnerability to happen rather than sheer ignorance/lack of experience. In such cases a tool which prevents this from happening in the first place is superior to one which doesn't have such a safety feature in it.
For the same reason we can't ever completely prevent traffic accidents by requiring higher skilled drivers. We can prevent traffic accidents by building cars, lanes, junctions and roads in such way which minimizes the damage caused by a human error.
I'll rather use a hammer which refuses to strike to my finger even if I try to make it to, rather than one which I can smash my fingers with by accident. I am sure you would too.
Sure, and that's why I e.g. prefer strong typing for bigger systems. The examples that have been used so far just aren't good examples of what is wrong with C++, which is what the point was about. At some point, there is a trade off between safety and power, and one that makes C++ quite well, IMO.
If those things are bad, why does the standard allow them? If any trained C++ programmer would know not to write that sort of code, what purpose does allowing it serve?
Because sometimes for loops are useful. It's no use trying to list up all use cases of for that are safe and/or useful, and prohibit those that aren't. There is a trade off between designing all sorts of safety checks into a language and raw power, and C++ mostly errs on the side of raw power. Which is largely why it's so prevalent and dominant.
I own a pneumatic nail gun that has two rather rudimentary safety features in the form of a trigger lock and a switch near the end of the 'barrel' the prevents it from being activated without the barrel pressing against something (in normal use, the wood you're nailing). It's rudimentary and still accidents happen with nail guns. I sometimes purposely circumvent the safety measures to get something done, e.g. when I'm shooting nails under a weird angle. It would be possible to think of many more safety features - allowing the gun to be operated only when activated with two hands (preventing one from shooting in one's hand), having all sorts of electronics that detect the surface that is being shot into, etc. Not a single one of guns would get sold because they cripple the way you work too much to be convenient.
"There is a trade off between designing all sorts of safety checks into a language and raw power"
This is a false dichotomy. Safety checks can be disabled if they become a performance problem in languages like Lisp. Safety checks can often be removed by a good compiler when the compiler can infer that the check will always be satisfied.
C++, however, provides nothing by default -- as opposed to being safe by default, and allowing programmers to be unsafe if they explicitly request that.
The more powerful features of C and C++ can be "bad" when misused or abused. Yes, that can happen when in the hands of a beginner.
There are times, however, when an experienced, knowledgeable user does need the power these features provide. Amazing things can be accomplished that couldn't otherwise be done when using a language like Java, C#, Ruby, Python, Perl, Go, Haskell, or Scheme.
Hey, the standard allows it. Why are we excluding code that is allowed by the standard and that many programmers would write if we are not making the "just do it right" argument?
If people treated C++ as a maintenance-only language and used it only for legacy code -- like COBOL -- the world would be a better place. The world would also save billions of dollars.
This is not a matter of RAII or "smart" pointers (which are not even smart enough to deal with cyclic references unless the programmer explicitly breaks the cycle). It is a matter of a language whose high-level features are constrained by low-level concerns, where the features compose poorly, and where things that are obvious (like requiring that functions with a non-void return type have a return value along every control path) are simply omitted from the standard. It is dizzying to think of how much money has been spent on bugs that were made possible by C or C++; why are we continuing to waste time and money on these languages?
You're ignoring the cost of actually running the applications in question. The benefits of reduced development costs can quickly be negated if performance starts to suffer.
We see this a lot with Ruby on Rails web apps, for instance. Perhaps they're quicker to develop in many cases, and maybe they're slightly less vulnerable to certain problems than C or C++ apps are, but they are much less efficient at runtime.
This inefficiency becomes visible when more hardware, or more powerful hardware, is needed to run such web apps. This inefficiency further becomes evident when users (it's worse when they're highly-paid employees) have to literally sit and wait for the web app to do its work. Over time, these costs can add up significantly.
It can be even worse for applications that are running on millions of systems. Even slight performance decreases can sum together to be very costly at such a scale.
C and C++ are still unmatched when it comes to producing efficient applications, both in terms of CPU usage and memory usage. Languages like Go and Rust may get close, but that'll be far in the future, if ever. I think it's safe to say that scripting languages like Perl, Ruby and Python will, in general, never be as efficient as C or C++.
Maybe you see C and C++ as a "waste of time and money", but they bring significant cost reductions for many of their users. That's why they're still being used today, and while they'll be used for a long time to come.
Yeah but in high performance web app the real competence for C and C++ are JVM languages (Java, Scala, etc.), not Ruby and Python. Twitter switched from Ruby to Scala/Java because Ruby performance was terrible.
Could they have used C/C++ for better performance? Yeah, but Java was goodEnough and for most high performance web apps it is GoodEnough, the cost being some more money for hardware (for Ruby and Python the cost is a lot higher and sometimes not even with expensive hardware you can solve the problems):
Just like C++ became GoodEnough and people switched to it from C for high performance apps, just like C became GoodEnough and we switched from assembly to C.
>If people treated C++ as a maintenance-only language and used it only for legacy code -- like COBOL -- the world would be a better place. The world would also save billions of dollars.
Citation needed. This is a wishful thinking idea that ignores tons of pragmatics in application development.
If all people started writing code in a modern C++ equivalent called Rust this would be a better world. After all in comparison Rust is safe by nature unlike C or C++. :)
You should not switch your apps. You should stop creating new projects in C++ once Rust is mature enough for your liking.
The fundamental "problem" we're having/facing with C and C++ is the investments we've put in. Lots of "infrastructure" in modern day computing relies on C and C++ and will do so for ages. We can't just drop the projects and switch to something else(say Rust or maybe Go), but we can stop creating new C and C++ codebases to alleviate the problem for the future.
Exactly, it's a chicken and egg problem. C++ has tons of libraries, while Rust has barely any. Rust can use external C bindings, but not C++ ones yet. I think if they solve the issue of using C++ libraries from within Rust, the transition will be much easier, and meanwhile more native Rust libraries will be created.
Yes, in the cases where the operating system is done in C++.
The C ABI is the operating system ABI and it is only ubiquitous in operating systems done in C. Other operating systems use whatever ABI the system offers.
How many Rust libraries are there for various tasks? What IDE supports Rust? What tutorials, books and conferences exist that teach and distribute the state of the art in development techniques? What example projects have been build in it to demonstrate its feasibility for large projects? What tools support Rust (continuous integration, code formatting/checking, build tools, ...)?
Well using make as an example of anything good is... let's say 'suspicious'. There is more to tooling than make and git (note that I deliberately omitted version control from my list)
Not really. The closest is Servo,[1][2] the massively-concurrent browser engine that the language was designed to create. But it's not very featureful yet, and is only a few thousand lines (compared to 100+ kloc for the Rust compiler itself).
It's too early yet to make claims like that. Rust may or may not end up supplanting C++ in some areas, but even in the most optimistic case it definitely won't obviate C++ entirely.
I've been working my way through the Rust tutorial. As much as I like the power and control it gives me, they really need to take a note from Go on the syntax. Some of the even fairly contrived examples are an eye-sore. I need to give it more time but yeesh there's a lot of syntax to look out for.
Not exactly related to syntax, but one of the issues I've found with following the tutorials is that there is only a handful working programs which actually show the feature in question in use. While I understand that (as the tutorial explicitly mentions) the tutorial is very terse and quick glance at the language features, I feel it could be improved tremendously if there was even one a few line fully compilable program exploring the features per section.
Then again I also understand that because the language changes and evolves, maintaining more code in the docs means more effort. However, this is one of the few things I've felt bugging me as an end-user. Of course I believe this can be expected to change for the better in the future as the language matures and stabilizes, but I figured I'd voice this out given this perfect opportunity. :)
That's a good idea, but it's going to require a bit of thought to implement. It might make sense to have an "advanced examples" external page for every section in the tutorial, which would sort of transform the current tutorial into an annotated table of contents. Which could be really neat! But perhaps it should wait until Rust has some analogue to play.golang.org available.
It will exist as long as there are people porting C to other architectures!
C is there since 1972, it is one of the most widely used programming languages of all time and there are very few computer architectures for which a C compiler does not exist. Many later languages have borrowed directly or indirectly from C, including C#, D, Go, Java, JavaScript, Limbo, LPC, Perl, PHP, Python, and Unix's C shell.
Nothing is future proof - don't worry. We have only been programming for the last 60 years. C has endured 40 years out of that. That is no guarantee that it will endure further. But the point is programming practices has not drastically changed during the course of these years. As and when a disruption occurs there, almost all our current tools shall be rendered obselete.
There is substantially more sophistication to his points than you give him credit for. If someone who is an expert in something - and he is - says something about their area of expertise that you think is obvious and simple, consider perhaps that it's your level of understanding that is lacking, not theirs.
Academics have been whining about C and C++ since at least the 1980s. But if you ask any three academics what programming languages they like, you'll get three different answers, depending on what department and program they are in.
I'm sure Dr. Regehr is a smart guy, but I don't consider academics good sources of advice on software engineering, for the same reason I don't get sex tips from Catholic priests. Also, John Regehr's CV relates more to static analysis than software engineering anyway.
It doesn't matter. This is not how programming works in the real world. In the real world, you write the most correct program you can under time pressure. A new compiler, operating system, or platform arrives that exposes a bug. You fix it and you move on. It doesn't matter if the language is future proof or not. The process is similar for any complex program.
The blog's name is "Embedded in Academia" and this is perfectly valid viewpoint for someone in academia to take. And people in academia should research towards building more robust tools and languages. But it really is not going to matter in the real world. Languages and platforms will always not be future proof because computing is complex.