Sunday, April 26, 2015

New EMC++ Excerpt Now Online

We've just put a new excerpt from Effective Modern C++ online. This time it's
  • Item 41: Consider pass by value for copyable parameters that are cheap to move and always copied.
It's available at the same place as all the other Items we've put up, namely here.

This Item grew out of my concern that move semantics is causing pass by value to lose its traditional reputation of being unreasonably expensive for user-defined types. More and more, I see coding examples from "thought leaders" (i.e., people who write articles or books or who give presentations at conferences, etc. -- you know, people like me :-}) where common types (e.g., std::vector, std::string, std::unique_ptr) are passed by value with nary a second thought. This worries me. I think it's a bad habit to get into. I blogged about why I think this is a particularly unjustifiable practice for move-only types like std::unique_ptr  here, and the ensuing comment stream makes for interesting reading, IMO.

What I really object to isn't the practice itself, but the risk that people will employ it without being aware of its consequences. I also worry about people taking an idea that can be reasonable under fairly constrained circumstances and overly generalizing it. In my view, pass by value is a strategy that should typically be considered only if (1) you have a parameter whose type is copyable (i.e., isn't a move-only type), (2) its move operations are cheap, and (3) the parameter will be unconditionally copied. Even then, I argue that you should only consider the use of pass by value, because even if all three criteria are fulfilled, there are still situations where it can incur a significant performance penalty.

I hope you find this latest excerpt from the book interesting and useful. I also hope you enjoy my finding a way to work allusions to both Mary Poppins and Jabberwocky into a single Item. I haven't checked, but I'd like to think I'm the only author who's found a reason to use supercalifragilisticexpialidocious in a book on C++.

Scott

Wednesday, April 22, 2015

The Time Needed to Write Effective Modern C++

Initial cover design.
Nobody asked me about the writing of Effective Modern C++ (EMC++), but I wanted to talk about it a little, so here we go.

In my now-somewhat-dated article for prospective technical book authors, I mention how much time authors invest in writing a book, with estimates ranging from 1.7 to 6 hours per finished book page. I was curious about how much time I spent writing EMC++, so I tracked it, sort of. I actually tracked the days where working on the book was my primary activity, and the result was that I spent 29 weeks from the day I started writing to the day I had a complete draft of the entire book. (The weeks were not always consecutive.) During these weeks, writing the book was essentially my full-time job.

If we figure a 40-hour work week, that'd yield about 1160 hours, but although writing EMC++ was my primary activity during those weeks, it wasn't my only activity. Let's knock that number down by 20% to account for my occasionally having to spend time on other things. That yields 928 hours to produce a full draft of the book.

Sending in files for publication.
During that time, Item and chapter drafts were being reviewed by outside readers, but I hadn't had time to revise the manuscript to take all their comments into account. Doing that (i.e., going from a full book draft to a "final" manuscript), revising the "final" manuscript to take the copyeditor's comments into account, and marking up the manuscript for indexing took another 11 weeks, i.e., 352 hours (again assuming an 80% time investment and 40-hour weeks). That yields a total of 1280 hours.

At that point, I'd marked up the manuscript for indexing by the publisher, but I hadn't reviewed the resulting index, nor had I reviewed the typeset pages or digital files for the rest of the book. That work took place in bits and pieces over the course of about 8 weeks. I didn't track my time, but I figure it took at least two full-time weeks on my part, so let's call it another 64 hours. That pushes the total "writing" time (which includes reviewing and processing comments from outside readers of pre-publication manuscripts as well as reviewing pre-publication files from the publisher) to about 1344 hours. Let's round up and call it 1350.

That amount of time, viewed as a full-time 40-hour-per-week job, corresponds to 33.75 weeks, which is a little under eight full-time months. EMC++ has about 310 final printed pages that I wrote (i.e., excluding pages whose content was generated entirely by the publisher), so my productivity was roughly 4.3 hours per final printed page.

My book has about 310 pages. Bjarne's fourth edition of The C++ Programming Language has about 1340. Do the math and marvel at the effort such a book requires. Even if he's twice as productive as I am, that represents 2880 hours--sixteen full-time months! I'm glad I don't have his job.

Before you can write a book on modern C++, you have to learn about C++11 and C++14. For me, that work started in 2009--four years before I felt able to write a book on it. Here are some EMC++-related milestones:
2009Started studying C++0x (the nascent C++11).
July 1, 2013Started writing what was then known as Effective C++11/14.
June 20, 2014Completed full draft of Effective Modern C++.
September 5, 2014Submitted final manuscript and index information to O'Reilly.
November 2, 2014Approved print and digital versions of the book for publication.
December 4, 2014Received first printed copy of EMC++.
As an aside, preparing materials for a technical talk generally takes me about 30 minutes per slide, so the one-hour talk I gave at CppCon last year probably took about 23 hours to put together.

Wednesday, April 8, 2015

More on ThreadRAII and Thread Suspension

On March 31, I did a webcast for O'Reilly on material from Effective Modern C++. (The webcast was recorded, and the recording is available here.) The focus of the presentation was how void futures can be used to communicate between threads in situations where condition variables and/or boolean flags might otherwise be used. The example I employed was starting a thread in a suspended state, and the code I ultimately built to was this:
{
  std::promise<void> p;                        // created first,
                                               // destroyed last
  std::thread t2([&p]
                 {
                   try {
                     p.get_future().get();
                     funcToRun();
                   }
                   catch(...) { … }
                 }
                );             

  ThreadRAII tr(std::move(t2),                  // created after p,
                ThreadRAII::DtorAction::join);  // destroyed before p

  …

  p.set_value();

  …

}                                   // if p hasn’t been set,tr’s
                                    // dtor hangs on a join
I explained that this code has the drawback that if control flows through this block such that the ThreadRAII object tr is created, but p.set_value() isn't executed (e.g., because an exception gets thrown), tr will hang in its destructor waiting to join with a thread that will never finish. For details, watch the webcast or consult pages 266-269 of the printed book (i.e., the second half of Item 39).

In Effective Modern C++, I conclude my discussion of this matter with "There are ways to address this problem, but I’ll leave them in the form of the hallowed exercise for the reader," and I refer to a blog post I made in December 2013 that, along with the comments it engendered, examines the issue in detail. In the webcast, I offer an explanation for why I don't show how to deal with the problem: "I don't know of a solution that's clean, simple, straightforward, [and] non-error-prone."

Michael Lindner took me to task for this. He wrote, "I was disappointed at the end of the webcast on void futures, because there was hype about void futures being useful, but the only example was a non-working one with no solution." Ouch. He went on to suggest that it may only be necessary to join with t2 if it's unsuspended, i.e., if funcToRun is permitted to run. If we don't need to synchronize with the completion of funcToRun, Michael suggested, we could do a detach instead of a join. This would mean that before the call to set_value, tr's DtorAction should be detach, and it should be set to join only after set_value has been called. Adding a setter to ThreadRAII for its DtorAction would make that possible, and the code could look like this:
{
  std::promise<void> p;
                       
  std::thread t2([fut = std::move(p.get_future())]
                 {
                   try {
                     fut.get();
                     funcToRun();
                   }
                   catch(...) { … }
                 }
                );

  ThreadRAII tr(std::move(t2),                      // before set_value is called,
                ThreadRAII::DtorAction::detach);    // configure tr's dtor to detach

  …

  p.set_value();
  tr.setDtorAction(ThreadRAII::DtorAction::join);   // after set_value is called,
                                                    // configure tr's dtor to join

  …

}                               // if p hasn't been set, its dtor sets an exception,
                                // thus preventing tr's dtor from hanging
With this approach, if set_value isn't called, p's destructor will write a std::future_error exception into the shared state that the future accesses, and that will cause the call to get in t2 to unblock. t2 will thus be able to run to completion. We won't know when t2 will complete, and we certainly can't guarantee that it will occur before the block containing tr finishes executing, but this will avoid the problem in my code whereby tr's destructor blocks waiting for a call to set_value that never occurs.

An interesting subtlety of this code is the lambda's capture clause, which performs what amounts to a move capture of the future produced by p. This is in contrast to the reference capture of p present in my code. My code performs a join on t2, and t2's lifetime is less than p's, so p must exist while t2 runs. It's therefore safe for t2 to refer to p. In Michael's approach, the detach means it's possible for t2 to continue running after p has been destroyed. It's thus critical that t2 not access p, and the move capture of p's future arranges for that to be the case. I'm grateful to Tomasz Kamiński for bringing this issue to my attention, though I'll note that Yehezkel Bernat suggested the same thing (presumably on stylistic grounds) in group chat during the webcast.

But what if we really need to do an unconditional join with t2--even if funcToRun is never permitted to execute? In that case, a detach-based approach won't work. The fact that std::promise's destructor writes an exception into its shared state seems like it should be able to be used as the basis of a solution, but there appears to be a chicken-and-egg problem:
  • In order to avoid having tr hang in its destructor, p must be destroyed before tr. 
  • That means that p must be defined after tr, because objects are destroyed in the inverse order of their construction.
  • tr must be initialized with the function its thread will execute, and that function must wait on a future that we provide.
  • To get a future, we need a std::promise.
  • That means that p must be defined before tr, because we need p to produce the future for initialization of tr.
  • Bullets 2 and 5 are contradictory.
Tomasz Kamiński sent me some code that showed how to avoid the apparent contradiction. The essence of his approach is to define tr before p, but to initialize it without a function to run on the std::thread it wraps. Without a function to run, there is no need for a future on which that function should block. After p has been defined, tr can receive an assignment that specifies the function to run and the action to perform in tr's destructor. This function can block on a future in the usual manner. Tomasz uses a class to encapsulate the various subtleties in his solution (e.g., tr must be declared before p, tr must be default-initialized and then assigned to). In this code (which is based on his implementation, but is not identical to it), I'm assuming that ThreadRAII has been revised to support default construction and that a default-constructed ThreadRAII object contains a default-constructed std::thread, i.e., a std::thread with no function to execute:
class SuspendedTask {
public:
  // FuncType should be callable as if of type void(std::future<void>)
  template<typename FuncType> 
  explicit SuspendedTask(FuncType&& f)
  {                         // no member init list ==> default-construct tr and p

    tr = ThreadRAII(std::thread([fut = p.get_future(), f = std::forward<FuncType>(f)]{ f(std::move(fut)); }), 
                    ThreadRAII::DtorAction::join); 
  }
 
  std::promise<void>& get_promise() { return p; }
 
private:
  ThreadRAII tr;            // tr must be listed 
  std::promise<void> p;     // before p!
};
This class can be used as follows:
{
  SuspendedTask t2([](std::future<void> fut){
                     try {
                       fut.get();
                       funcToRun();
                     }
                     catch(...) { … }
                   });

  …

  t2.get_promise().set_value();

  …

}
A key element of this design is a class that bundles together a std::thread (inside the ThreadRAII object) and a std::promise. That idea was the crux of the first comment on my December 2013 blog post; it was made by Rein Halbersma. Other commenters on that post embraced the same idea, but as the thread went on, things got increasingly complicated, and by the end, I wasn't really happy with any of the solutions presented there. That's why I didn't use any of them in the book. In retrospect, I should have spent more time on this problem.

I'm grateful to Michael Lindner and Tomasz Kamiński for pushing me to revisit the problem of thread suspension through void futures, ThreadRAII, and exception-safe code.

Friday, April 3, 2015

Materials Update for my C++11/14 and my C++-in-Embedded Training Courses

Artima just pushed out the latest updates to my annotated training materials on C++11/14 and on making effective use of C++ in an embedded environment. These materials were originally published in 2010, and in the ensuing five years, I've updated the C++11/14 materials nine times and the C++-in-embedded materials twice--a total of ten and three releases, respectively.

From the beginning, the rule has been that buyers are entitled to free updates to the materials as long as I produce them, so if you own a copy of either (or both) set/sets of materials, you should have received email notification from Artima that shiny, new PDF is waiting for you in your Artima account.

I don't plan to update these materials further. C++11 and 14 have been finalized, and I think by the time C++ is updated again (a process that, practically speaking, is likely to occur both through technical specifications as well as a revised language standard), training courses covering the latest features will assume that people have a background in much of C++11 and C++14. As regards C++ in embedded systems, my current materials focus on C++98/03, because my experience has been that embedded shops tend to be more conservative in their adoption of new compilers (and the language features they offer) than their non-embedded counterparts. Going forward, I think it would be prudent for training courses for embedded developers to assume that C++11 features are (or soon will be) available, and I don't plan to revise my training course to adopt that stance. (That's because my current C++-related work builds on my experience with and the information in Effective Modern C++.)

I think the just-published versions of these training courses correspond well to the practical state of C++ as of 2015. If you've purchased copies of these materials, I hope you find the latest updates useful. (You should have received a copy of the changelog in the email telling you about the availability of the new versions.) If you don't have copies of these materials, I think they're a good way to educate yourself about the topics they address. As always, you can download free samples of each set of materials from the following links:
Scott