If maintaining backward compatibility is important, then introducing a new keyword into a programming language is always a serious affair. Introducing a new keyword that is already used in millions of lines of existing code as an identifier (class name, namespace name, variable name, etc), can break a lot of product code and discourage people from upgrading their compilers and using the new language/library features.
Unlike other languages, backward compatibility is a “feature” (not a “meh“) in C++. Thus, the proposed introduction of a new keyword is highly scrutinized by the standards committee when core language improvements are considered. This is the main reason why the verbosity-reducing “auto” keyword wasn’t added until the C++11 standard was hatched.
Even though Bjarne Stroustrup designed/implemented “auto” in C++ over 30 years ago to provide for convenient compiler type deduction, the priority for backward compatibility with C caused it to be sh*t-canned for decades. However, since (hopefully) nobody uses it in C code anymore (“auto” is the default storage type for all C local variables – so why be redundant?), the time was deemed right to include it in the C++11 release. Of course, some really, really old C/C++ code will get broken when compiled under a new C++11 compiler, but the breakage won’t be as dire as if “auto” had made it into the earlier C++98 or C++03 standards.
With the seriousness of keyword introduction in mind, one might wonder why the “override” and “final” keywords were added to C++11. Surely, they’re so common that millions of lines of C/C++ legacy code will get broken. D’oh!
But wait! To severely limit code-breakage, the “override” and “final” keywords are defined to be context sensitive. Unless they’re used in a purposefully narrow, position-specific way, C++11 compilers will reject the source code that contains them.
The “final” keyword (used to explicitly prevent further inheritance and/or virtual function overriding) can only be used to qualify a class definition or a virtual function declaration. The closely related “override” keyword (used to prevent careless mistakes when declaring a virtual function) can only be used to qualify a virtual function. The figure below shows how “final” and “override” clarify programmer intention and prevent accidental mistakes.
Because of the context-specific constraints imposed on the “final” and “override” keywords, this (crappy) code compiles fine:
The point of this last code fragment is to show that the introduction of context-specific keywords is highly unlikely to break existing code. And that’s a good thing.
With the addition of the <chrono> and <thread> libraries to the C++11 standard library, programs that require precise, time-synchronized functionality can now be written without having to include any external, non-standard libraries (ACE, Boost, Poco, pthread, etc) into the development environment. However, learning and using the new APIs correctly can be a non-trivial undertaking. But, there’s a reason for having these rich and powerful APIs:
The time facilities are intended to efficiently support uses deep in the system; they do not provide convenience facilities to help you maintain your social calendar. In fact, the time facilities originated with the stringent needs of high-energy physics. Furthermore, language facilities for dealing with short time spans (e.g., nanoseconds) must not themselves take significant time. Consequently, the <chrono> facilities are not simple, but many uses of those facilities can be very simple. – Bjarne Stroustrup (The C++ Programming Language, Fourth Edition).
Many soft and hard real-time applications require chunks of work to be done at precise, periodic intervals during run-time. The figure below models such a program. At the start of each fixed time interval T, “doStuff()” is invoked to perform an application-specific unit of work. Upon completion of the work, the program (or task/thread) “sleeps” under the care of the OS until the next interval start time occurs. If the chunk of work doesn’t complete before the next interval starts, or accurate, interval-to-interval periodicity is not maintained during operation, then the program is deemed to have “failed“. Whether the failure is catastrophic or merely a recoverable blip is application-specific.
Although the C++11 time facilities support time and duration manipulations down to the nanosecond level of precision, the actual accuracy of code that employs the API is a function of the precision and accuracy of the underlying hardware and OS platform. The C++11 API simply serves as a standard, portable bridge between the coder and the platform.
The figure below shows the effect of platform inaccuracy on programs that need to maintain a fixed, periodic timeline. Instead of “waking up” at perfectly constant intervals of T, some jitter is introduced by the platform. If the jitter is not compensated for, the program will cumulatively drift further and further away from “real-time” as it runs. Even if jitter compensation is applied on an interval by interval basis to prevent drift-creep, there will always be some time inaccuracy present in the application due to the hardware and OS limitations. Again, the specific application dictates whether the timing inaccuracy is catastrophic or merely a nuisance to be ignored.
The simple UML activity diagram below shows the cruxt of what must be done to measure the “wake-up error” on a given platform.
On each pass through the loop:
- The OS wakes us up (via <thread>)
- We retrieve the current time (via <chrono>)
- We compute the error between the current time and the desired time (via <chrono>)
- We compute the next desired wake-up time (via <chrono>)
- We tell the OS to put us to sleep until the desired wake-up time occurs (via <thread>)
For your viewing and critiquing pleasure, the source code for a simple, portable C++11 program that measures average “wakeup error” is shown here:
The figure below shows the results from a typical program run on a Win7 (VC++13) and Linux (GCC 4.7.2) platform. Note that the clock precision on the two platforms are different. Also note that in the worst case, the 100 millisecond period I randomly chose for the run is maintained to within an average of .1 percent over the 30 second test run. Of course, your mileage may vary depending on your platform specifics and what other services/daemons are running on that platform.
The purpose of writing and running this test program was not to come up with some definitive, quantitative results. It was simply to learn and play around with the <chrono> and <thread> APIs; and to discover how qualitatively well the software and hardware stacks can accommodate software components that require doing chunks of work on fixed, periodic, time boundaries.
In case anyone wants to take the source code and run with it, I’ve attached it to the site as a pdf: periodicTask.pdf. You could simulate chunks of work being done within each interval by placing a std::this_thread::sleep_for() within the std::this_thread::sleep_until() loop. You could elevate numLoops and intervalPeriodMillis from compile time constants to command line arguments. You can use std::minmax_element() on the wakeup error samples.
Standardization is long periods of mind-numbing boredom interrupted by moments of sheer terror – Bjarne Stroustrup
In his ACCU2013 talk, “C++14 Early Thoughts“, Bjarne Stroustrup presented this slide:
By using “we” in each bullet point, Bjarne was referring to the ISO WG-1 C++ committee and the daunting challenges it faces to successfully move the language forward.
Not only do the committee leaders have to manage the external onslaught of demands from a huge, dedicated user base, they have to homogenize the internal communications amongst many smart and assertive members. To illustrate the internal management problem, Bjarne said something akin to: “There is no topic the committee isn’t willing to discuss at length for two years“.
In order to prevent being overwhelmed with work, the committee uses this set of grass roots principles to filter out the incoming chaff from the wheat:
I have no idea how internal conflicts are handled, nor how infinite loops of technical debate are exited, but since the all-volunteer committee is still functioning and doing a great job (IMO) of modernizing the language, there’s got to be some magic at work here:
Temporary and Employee base class “objects” get paid by the company to do work that directly creates value. Manager objects inherit an Employee’s responsibilities and encapsulate new, manager-specific, behaviors; like monitoring/commanding/reprimanding non-manager Employees, Temps, and Assistants. Proceeding down the inheritance tree on the right, a Director object inherits the behaviors of both the Manager and Employee classes while adding new “directorial” behaviors.
When BD00 saw Bjarne’s inheritance tree example, he said to himself “Dude, you got it wrong. If you wanted to model the real world, here’s what you shoulda presented“:
It woulda added a touch of edgy humor to the book, dontcha think?
When I write my first programming book, I’m gonna have diagrams like that and code fragments like this in it:
I’m thinking of hatching a kickstarter.com project and titling the hybrid book something like “Management Idiots And Programming Idioms“. What would you name it, and would you buy it?
Since I have the privilege of using C++11/14 on my current project, I’ve been using the new language idioms as fast as I can discover and learn them. For example, instead of writing risky, exception-unsafe, naked “new“, code like this:
I’ve been writing code like this instead:
By using std::unique_ptr instead of a naked pointer, I don’t have to veer away from the local code I’m writing to write matching delete statements in destructors or in catch() exception clauses to prevent inadvertent memory leaks.
I could’ve used a std::shared_ptr (which can be copied instead of “moved“) in place of the std::unique_ptr, but std::shared_ptr is required to maintain a fatter internal state in the form of strong and weak owner counters. Unless I really need shared ownership of a dynamically allocated object, which I haven’t so far, I stick to the slimmer and more performant std::unique_ptr.
When I first wrote the std::unique_ptr code above, I was concerned that using the std::move() function to transfer encapsulated memory ownership into the safeTgtList vector would add some runtime overhead to the code (relative to the C++98/03 style of simply copying the naked pointer into the scaryTgtList vector). It is, after all, a function, so I thought it must insert some code into my own code.
However, after digging a little deeper into my concern, I discovered (via Stroustrup, Sutter, and Meyers) that std::move() adds zero runtime overhead to the code. Its use is equivalent to performing a static_cast on its argument – which is evaluated at compile time.
As Scott Meyers stated at GoingNative13, std::move() doesn’t really move anything. It simply prepares for a subsequent real move by casting its argument from an lvalue to an rvalue – which is required for movement of an object’s innards. In the previous code, the move is actually performed within the std::vector::emplace_back() function.
Quoting Scott Meyers: “think of std::move() as an rvalue_cast“. I’m not sure why the ISO C++ committee didn’t define a new rvalue_cast keyword instead of std::move() to drive home the point that no runtime overhead is imposed, but I’d speculate that the issue was debated. Perhaps they thought rvalue_cast was too technical a term for most users?
As I said early in the post, the code example is “like” the code I’ve been writing. The real code that triggered this post is as shown here:
Since each “entry” CfarDetState object must be uniquely intialized form it’s associated CfarCrossing object, I can’t simply insert estd::make_unique<CfarDetState>() into the emplace_back() function. All of the members of each “entry” must be initialized first. Regardless of whether I use emplace_back() or push_back(), std::move(entry) must be used as the argument of the chosen function.
If you didn’t already know it, I’m a fan of the C++ programming language. Of course, not everybody feels the same way. There are many smart people who are among the “haters“.
C++ is a horrible language. It’s made more horrible by the fact that a lot of substandard programmers use it, to the point where it’s much much easier to generate total and utter crap with it. – Linus Torvalds
When I read anti-C++ tirades like Linus Torvalds’ emotionally charged attack, it always stings a little at first. But then I eventually step back from the “emotional-me” and remember (thanks to the teachings of Byron Katie and Eckart Tolle) that the world will never be the way I “demand!” it to be. The length of time it takes me to disengage from abstract thought-storms like these and return to earth is proportional to how deeply I’m attached to one side of the debate stomping around in my head.
“The test of a first-rate intelligence is the ability to hold two opposing ideas in mind at the same time and still retain the ability to function.” – F. Scott Fitzgerald
Java creator James Gosling, Haskell‘s Bartosz Milewski, and Go creator Rob Pike are three of the more prominent people in the anti-C++ camp. As expected, they have agendas to promote: advocating their own favorite programming languages at the expense of C++.
- Milewski’s rant: “Edward C++Hands“
- Pike’s rant: “Less Is Exponentially More“
- Gosling’s rant: “The Java Language Environment“
Ironically, these rants by smart and well known people can be construed as tributes to C++. That’s probably why this is one of my favorite Bjarne Stroustrup quotes:
There are just two kinds of languages: the ones everybody complains about and the ones nobody use.
Herb Sutter is by far the biggest cheerleader for the C++ programming language – even more so than the language’s soft spoken creator, Bjarne Stroustrup. Herb speaks with such enthusiasm and optimism that it’s infectious.
In his talk at the recently concluded GoingNative2013 C++ conference, Herb presented this slide to convey the structure of the ISO C++ Working Group 21 (WG21):
On the left, we have the language core and language evolution working groups. On the right, we have the standard library working group.
But wait! That was the organizational structure as of 18 months ago. As of now, we have this decomposition:
As you can see, there’s a lot of volunteer effort being applied to the evolution of the language – especially in the domain of libraries. In fact, most of the core language features on the left side exist to support the development of the upcoming libraries on the right.
In addition to the forthcoming minor 2014 release of C++, which adds a handful of new features and fixes some bugs/omissions from C++11, the next major release is slated for 2017. Of course, we won’t get all of the features and libraries enumerated on the slide, but the future looks bright for C++.
The biggest challenge for Herb et al will be to ensure the conceptual integrity of the language as a whole remains intact in spite of the ambitious growth plan. The faster the growth, the higher the chance of the wheels falling off the bus.
“The entire system also must have conceptual integrity, and that requires a system architect to design it all, from the top down.” – Fred Brooks
“Who advocates … for the product itself—its conceptual integrity, its efficiency, its economy, its robustness? Often, no one.” – Fred Brooks
I’m not a fan of committees in general, but in this specific case I’m confident that Herb, Bjarne, and their fellow WG21 friends can pull it off. I think they did a great job on C++11 and I think they’ll perform just as admirably in hatching future C++ releases.
Being a slow learner, BD00 finds it easier to learn a new topic by researching the writings from multiple sources of authority. BD00 then integrates his findings into his bug-riddled brain as “the truth as he sees it” (not the absolute truth – which is unknowable). The different viewpoints offered up by several experts on a subject tend to fill in holes of understanding that would otherwise go unaddressed. Thus, since BD00 wanted to learn more about how C++11′s new “move” and “rvalue reference” features work, he gathered several snippets on the subject and hoisted them here.
The motivation for adding “rvalue” references and “move” semantics to C++11 to complement its native “copy” semantics was to dramatically improve performance when large amounts of heap-based data need to be transferred from one class object to another AND it is known that preserving the “from” class object’s data is unnecessary (e.g. returning a non-static local object from a function). Rather than laboriously copying each of a million objects from one object to another, one can now simply “move” them.
Unlike its state after a “copy“, a moved-from object’s data is no longer present for further use downstream in the program. It’s like when I give you my phone. I don’t make a copy of it and hand it over to you. After I “move” it to you, I’m sh*t outta luck if I want to call my shrink – until I get a new phone.
Chapter 13 – C++ Primary, Fifth Edition, Lippman, Lajoie, Moo
Chapter 3 – The C++ Programming Language, 4th Edition, Bjarne Stroustrup
Chapter 3 – The C++ Standard Library, 2nd Edition, Nicolai M. Josuttis
Overview Of The New C++ (C++11), Scott Meyers
Did you know that…
I clipped this snippet from the slides that accompanied this interview: “David Intersimone of Embarcadero interviews Bjarne Stroustrup to to kick off their CodeRage 7 conference.”