Herb Sutter writes about C++, including the Guru of the Week series, and about concurrency in software.
HN commenters cite his C++ articles and books for best practices and the class interface principle, and his 2005 article on the move to concurrency.
The megahertz-scaling "Free Lunch" was declared dead 15 years ago [http://www.gotw.ca/publications/concurrency-ddj.htm] and it's been only getting deader. People are finally, grudgingly accepting that they must go parallel unless we want to see software performance stagnate permanently. For most people here, the issue has been obvious since before they learned to program. But, still they are putting off learning how to deal with it. The first, obvious answer to that is threading. But, in my experience, SIMD is a bigger bang for the buck for two reasons: 1) No synchronization problems. 2) Better cache utilization. It's not just that SIMD forces you to work in large, contiguous blocks. Fun fact: When you aren't using SIMD you are only using a fraction of your L1 cache bandwidth!
A big challenge is that SIMD intrinsic-function APIs are weird. They have inscrutable function names and sometimes difficult semantics. What helped me greatly was going through the effort of writing #define wrappers for myself that just gave each function in SSE1-3 names that made sense to me. I don't expect many people to put in that effort. And, unfortunately, I don't have go-to recommendations for pre-existing libraries. Best I can do is:
https://github.com/VcDevel/Vc is working on being standardized into C++. It's great for processing medium-to-large arrays.
https://ispc.github.io/ is great for writing large, complicated SIMD features.
https://github.com/microsoft/DirectXMath is…
> RAII can be decoupled from ctor/dtor mechanism, see Python's 'with' statement
That may be a useful language feature, but I don't count that as full RAII. I'm inclined to agree with Wikipedia's definition that the destructor must run whenever the object's lifetime ends, to count as proper RAII:
> resource deallocation (release) is done during object destruction (specifically finalization), by the destructor
As x can presumably leak from the with block of your example, it isn't full RAII. I think it's equivalent to the confusingly named using feature of C#. [0]
> throwing exceptions from ctors or, worse, dtors. Herb Sutter used to have a loooong list of what can go wrong in such situations
Thanks for the pointer ( or should that be reference? :-P ), these are an interesting read. [1][2][3]
> Explicit error codes happen to make it impossible to write ctors / dtors that can enter an error state by virtue of the fact that there is no way to return the error
Yes, but this approach detracts from the RAII somewhat, as it means the programmer must move the substantial code out of the constructor and into a post-construction initialization member-function capable of throwing. That may be a price worth paying, but I do consider it a price, as we've lost a useful invariant: we must now keep track of which instances are in the constructed-but-unready state. An unfortunate slide back toward C.
> My suspicion is that this is 50% of the reason of banning C++ exceptions in solid…
Signed arithmetic is generally only problematic when you hit the upper or lower bound. The right answer is almost never to use unsigned; instead, it's to use a wider signed type.
It's far too easy to get things wrong when you add unsigned integers into the mix; ever compare a size_t with a ptrdiff_t? Comes up all the time when you're working with resizable buffers, arrays, etc.
And no, Java did not choose signed by default because of legacy. http://www.gotw.ca/publications/c_family_interview.htm:
"Gosling: For me as a language designer, which I don't really count myself as these days, what "simple" really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn't -- and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up."
Unsigned is useful in a handful of situations: when on a 32-bit machine dealing with >2GB of address space; bit twiddling where you don't want any sign extension interference; and hardware / network / file protocols and formats where things are defined as unsigned quantities. But most of the time, it's more trouble than it's worth.
> you should probably prefer the first definition ("go around, past the edge of") instead of the fourth definition, as you've done here.
That was the third definition. The first definition was "a woman's outer garment fastened around the waist and hanging down around the legs." /snark
> we should look more at end-user satisfaction and less at often pedantic technical analyses of solutions.
They aren't orthogonal. User satisfaction comes from a smooth experience. Regardless of how shiny something is, if it slows down the rest of the system (or is itself laggy) users will notice. I choose to run foobar instead of other music players because it's small and fast.
> are we further limiting a great web UX to those with the money to afford the latest laptops?
This is what infuriates me. People have been talking about how the "free lunch" is over [1] since 2009. About how CPUs have stopped getting faster, and how we should make the best out of the current hardware by learning to paralelize. Yet javascript is the most popular language and it's fundamentally single threaded. High quality software is not about having "a great web UX". VLC doesn't have it (great web UX). Blender doesn't have it. Intellij IDEA doesn't have it. And they're not only extremely complex and efficient softwares, they also have great UIs. Our computers are extremly powerful. The fact that someone would wonder if they're not enough to run pretty clipboard managers and fancy text editors is making me…
I have a prediction about where this will go:
First, things will continue about the way they are now: cores will stay at the same speed or get slower but there will be more of them.
The industry will stay this way for at least the next 5 to 10 years, and during that time we are going to get better and better figuring out how to take advantage of the increasing number of cores.
Finally, at some point in the future (I predict between 10 and 15 years), we are going to see an explosion in materials science that allows us to suddenly jump from 3 - 5GHz / core to 400 GHz - 5 THz per core (with even more cores than we have now and dramatically lower power consumption).
At this point we will be nicely prepared to exploit both the parallelism and the return of raw speed gains. We'll also see completely different architectures like Chuck Moore's greenArrays, quantum co-processors, "memory/compute fabrics" like memristor crossbar arrays, and further advancements in the GPU/APU area. These changes will continue to challenge our assumptions about what are the "proper" paradigms, languages, and architectures for computer programs.
We've made great progress already. I can think of myself as an example: in 2005 I, like most of the programmers here, read Herb Sutter's essay The Free Lunch Is Over[1] and realized that he was correct: the era of easy speedups was giving way to the era of concurrency. And programmers were going to have to figure out how to take advantage of that…
While I do not disagree with Fowler's criterion as being one of several to consider, The ability of function (and identifier) naming to make code "easy to understand at a glance" is frequently overstated, including in this article. Once code reaches a size where it is moderately complicated, the reasons for some action being done in a particular way are not necessarily locally obvious (or obvious at all.) One common reason for this is that things are being done in a particular way to conform to a contract that is relied upon in multiple, disparate parts of the program (and any reasonably large program does have such contracts, even if it was not produced by an explicitly design-by-contract methodology.)
For a flavor of how this works, read the article [1] below, in response to Tom Cargill's classic article 'Exception Handling: A False Sense of Security'. Even though the code example here is small enough to be understood in one go, I doubt that the reasons for it being the way it is are immediately obvious to most programmers, unless they have been primed by exposure to the issue before (note that the first answer given was not entirely correct.)
[1] http://www.gotw.ca/gotw/008.htm