And I'd hardly label C++ as the epitome of expressiveness. A broad, haphazard feature set doesn't necessarily imply it's expressive.
And I'd hardly label C++ as the epitome of expressiveness. A broad, haphazard feature set doesn't necessarily imply it's expressive.
But you can certainly have automatic memory management in C++ as well. Stack-allocated objects manage themselves, for dynamically allocated objects one can use scoped/shared pointers. One added perk is that you can extend this approach to deterministically manage not only memory, but any other resource that can be acquired and released (e.g. locks). I actually like this approach very much. The only problem I see there is that since you're still using raw pointers, the runtime can't really move things around for efficiency since that would invalidate the pointers. But in a garbage-collected language that doesn't expose pointers, the heap can be defragmented during runtime, which is nice.
The biggest downside is not really efficiency but that you lose the memory safety of a managed language doing this.
I wonder if it's possible to design a language that would be able to prevent these problems at compile time without incurring the runtime cost of managed languages. I mean, we already can detect some problems using static code checking tools (i.e. PVS-studio), so it must be feasible.
Rust does this. :) It is not easy to get right—the borrow checker required a lot of design work to achieve the right balance between expressiveness and practicality—but I think we've shown that it is possible.
(Disclaimer: I work on Rust.)
Arrays are bounded. You can usually disable bounds checking locally if you really need the extra ms, for cases the compiler is not able to optimize them.
Proper strings instead of char pointers that might be null terminated or not.
Var parameters and unbounded vector parameters to handle arguments that need to be changed.
Oberon family has already proven it is possible to write desktop operating systems in GC enabled systems programming languages.
Now as you say, many of these things are possible in modern C++. The problem is getting everyone on board, specially when dealing with old code bases littered with C like constructs.
Garbage collection has a ton of problems in systems applications (off the top of my head: unreasonable and unpredictable delays, incapability of dealing with a large amount of heap-allocated memory efficiently). Reference counting is better in many ways, but is not always appropriate and definitely has a cost (and I say this as someone who uses shared_ptr quite often).
Some people eschew Java and the JVM due to the spectre of garbage collection ruining their quasi-realtime system. But others embrace the JVM, organizing their programs to avoid garbage collection during critical times (which could last for hours or days).
With C and C++ it is the same, but backward: people flock to these languages for the predictable performance. Yet once there, they discover they must avoid the default allocator, for it is a source of unpredictable delays. Pooled allocators are created, and the end result is much the same as for the JVM folks.
http://en.cppreference.com/w/cpp/container/vector/reserve
If you want realtime guarantees, you have a lot more unpredictable problems, like CPUs having cache.
Not always. If we have a loop that calls our append_10_items_to_vector N times (on the same vector), then your reserve() just transformed the loop into O(N^2) code. Without reserve, it would be O(N).
As a rule of thumb, use reserve only if you are really sure that nobody's ever going to add anything more to your vector in the future.
However, almost always, somebody has to write that database code and the code that displays the nicely formatted data (like in actually lighting pixels on the screen instead of filling out some css properties) and even the code that takes the user input. And for these purposes managed memory is a disaster and C++ is the most expressive language available.
IMHO, managed memory for system-level code is nowhere near being a "disaster". It might be sometimes tricky to get right, but not any harder than getting manual memory management right in C++ (and smart_pointers aren't a silver bullet, neither). Additionally, there are some Java extensions that let you write real-time code in it and guarantee GC won't kick in when you don't want it to.
I write code that I consider system level (it talks to the hardware directly) and "tricky" is quite an understatement. Starting from the fact that there are half a dozen types of memory (not even talking about memory-mapped I/O) and just one "heap" in Java. Not to mention the hardware is not aware about the awesomeness of JVM and is trying to read/write actual memory pointers. I can imagine that you could get around this (as well as the lack of bit-fields and unsigned types) through some kind of memory-mapped-as-a-file trick but at this point you are not using the memory management at all. And IMHO this is very far from being "not any harder" than using pointers in C++.
You'd been saying that in your humble opinion system programming in Java is no harder than in C++, which is something very alien to my experience of actually programming on the system level (as same as your performance claims but I am not even going to get into this one. Last time I did a Java programmer told me that a Java program that is measuring as 4 times slower is running "on the same level", whatever that means).
As of performance, they are doing extremely well. Last time I checked Netty vs Nginx was a tie; Tomcat vs Apache HTTP were also extremely close; Apache Spark (pure Java) is generally much faster than Impala (using C++); and Cassandra has no direct competitors written in C++ so far (not counting 1000 toy databases here), but it is definitely one of the top performant NoSQL stores out there.
For the concrete addresses you just link with the symbols defined outside of your C code. Special ops cannot be done in pure standard, but this is not an issue in practice as there are no pure standard compilers. Even for app development you want intrinsics just for the vector, cache and bit instructions.
You need an external Assembler or OS APIs to provide the required functionality. The same way that a Java runtime library can provide bindings to Assembly or OS APIs.
There are commercial AOT native compilers for Java targeting systems programming, like the ones from Atego.
Many mainstream languages, with AOT native compilers would be as usable.
Do you write portable code in C and C++ across disparate systems and OSs, using multiple compilers?
I used to do that between 1994 and 2001. Don't miss those days of hit-and-miss about compiler support.
> We did not have a standard till 1998, yet somehow managed to use C++.
Yes we did. It was called the Annotated Reference Manual, used as basis for the standard, coupled with articles from The C++ Report, The C Users Journal (latter The C/C++ Users Journal).
> Same as with C, which had been used very successfully standard-less for almost two decades.
It works if you only care about one specific compiler. On those days the standard was the AT&T UNIX compiler.
And then you realize it's not a big deal. Changes between platforms are greater than changes between compilers. When your targets have different endianness or, even better, mixed endianness between different PUs - the standard compliance of your code is the least of your worries.
>It works if you only care about one specific compiler.
Also works if you care about a finite number of specific compilers and architectures.
>On those days the standard was the AT&T UNIX compiler.
There were, probably, hundreds of compilers and C flavors even before the ANSI C. What one thought was "the standard" was very subjective.
Sure, but the goal is to minimize system dependencies as much as possible.
Relying on language extensions works against it.
> There were, probably, hundreds of compilers and C flavors even before the ANSI C. What one thought was "the standard" was very subjective.
Yes, I do remember variants like Small C.
Excuse me but what??? Please give an example. Except for maybe setting up an isr vector table and the odd coprocessor command in assembler, all other peripheral functionality is usually memory mapped and perfectly accessible in C and C++.
As of XNU, it is mostly pure C. There are a few C++ files there but they are very far from what we call "modern C++". No templates, no use of standard library, no exceptions, const char* everywhere. Just C with classes.
You are correct, C++ is a bad choice for low-level device drivers. But it's getting better [1]!
[1] http://msdn.microsoft.com/en-us/windows/hardware/gg487420.as...
But when you're working in a constrained environment, you need the option to control all this in much detail. C++ is one of the very few options you have for that.
I agree that if your memory management is dead simple and you are fine with using only unique_ptr everywhere, C++ solution is superior. You obviously don't need GC, so why pay for it. Probably C would do just as fine, assuming you have some static analysis tool to find all the forgotten free calls.
However, there are some domains where GC enables completely different ways of programming. E.g. lockless concurrency / immutable structures / functional programming etc. Poor shared_ptr performance and scalability is prohibitive in those areas.
I don't believe such a static analysis tool can exist without being a different language (e.g. Cyclone). There just isn't enough information about ownership semantics in C's type system.
[1] http://static.rust-lang.org/doc/0.8/rust.html#memory-boxes
While it helps, it can only cover the code you have access to.
No way to take care of third party libraries delivered in binary form.
Java's pointer might be faster than shared_ptr. But, in Java, all objects are held by pointers. C++ gives you more options. It's not like we're going "Damn, I just can't figure out the ownership here. I'll just use shared_ptr and be done with it" every day. In my experience, usage of shared_ptr is quite limited.
shared_ptr use is limited because shared_ptr is slow and people learned to avoid it at all cost. But having to think about ownership influences design choices and often makes things more complex and less performant that needed (e.g. it often forces to copy objects instead to just pass a pointer).
You can keep extolling the virtues of Java performance, but fact remains that Java doesn't see much use in areas where performance is critical. It doesn't mean Java doesn't have a use, it's just not there.
Maybe this is the reason C++ guys always complicate things: http://www.johndcook.com/blog/2011/06/14/why-do-c-folks-make...
As of Java being not-performant enough: that's why Java is used as one of the most popular language for implementation of modern NoSQL databases systems. Everyone knows performance doesn't matter in databases. ;)
without these i have very few leaks or problems - and when i do they are trivial to find and fix. i see the advantage for RAD, but for performance and memory critical code these things are actually dangerous and stupid imo. measurably so...