Tuesday, February 4, 2014

Status Report on Effective C++11/14

About a year ago, I blogged, "If everything falls into place the way I hope it will, there will be a book about 10 months from now. If. I'm not making any promises." Smart me: it's been 12 months, and there's still no book. In part, that's because I decided to expand the scope of the project to encompass C++14 as well as C++11. That required that I familiarize myself with C++14, which has only recently really settled down. Another factor is that writing the Items for the book has proved to be slower going than I expected. I thought my researching days were behind me, but I've spent many more days tracking down language details than I'd predicted.

But I've made progress. I have full drafts of 26 Items, and I have a candidate list of 25 more. Most of the candidate Items won't make the final cut, because it's looking like the book will end up with around 40 Items total. That's due to space limitations. My prior books have run up to around 90,000 words total (about 300 pages) , and that's the length limit I'm imposing on this one.I currently have about 54,000 words, so by that measure, I'm about 60% done. That's a high estimate of how much is complete, unfortunately, because feedback I've received on existing Items reveals that some require significant rework before they'll be ready to be reviewed again.

As things stand now, I'm thinking I should be done by the beginning of June. (Between now and then, writing the book is essentially my full-time job.) "Done" is a funny word, however, because it simply means I've declared my manuscript ready to publish. That's step one in a multi-step process that ultimately leads to printed and digital books. My document has to be translated from the format I use (MS Word) into its final forms (probably print PDF, digital PDF, ePub, and Kindle), and along the way it may get translated into XML as an intermediary format. I'll spare you the details, but suffice it to say that these translations will be largely custom-made for my book, and that means they'll take time to develop and verify. I won't be doing them myself, though I'll be quite involved in making sure they produce the proper output.

I'm hoping that digital versions of the book will be available this summer, but don't press me on exactly what "summer" means. Print books will probably take a bit longer, so call my hope for availability "late summer,"  where neither "late" nor "summer" is well-defined.

In case you're interested in a glimpse into the process of writing this book, I've made available two versions of the Item I finished writing just today, "Declare functions noexcept whenever possible."
  • A first draft with my comments on it. My usual approach to producing an Item is to write and edit it on-screen until it looks decent, then print it out and mark it up, and finally make the edits I've marked. I then print it again, mark it up again, and...rinse, lather, repeat. Generally, it takes about three iterations before I think I've got something ready for the next step, which is to make sure the code compiles. After fixing any simple coding errors, I forward the Item to outside reviewers for their feedback. If the code has errors that are serious enough to affect the content of the Item, I do the necessary rewrite, then fall back into the print/markup/edit loop.
  • The reviewable draft, i.e., the document I've just sent to some outside reviewers for their comments. In this case, that includes you. Feel free to email me your comments and suggestions for improvement, or post them as comments on this blog post.
At some point later, I'll post the current status of the table of contents. Given that I'm going to have to jettison a large number of Items in the current outline, I don't see the point in doing that now. I'll do it once I have a list that looks more stable.

And now, if you'll excuse me, I have a word processor waiting for me...

Scott

16 comments:

K-ballo said...

"Why? Because it permits compilers to generate better code." Not only that, but since it's part of the API and it can be queried, it permits libraries to take different approaches based on the `noexcept` guarantee. An example of this is `std::vector`, which will not move unless the operation is noexcept. Also related, `std::move_if_noexcept`.

Scott Meyers said...

@K-ballo: Other than uses related to std::move_if_noexcept (which is discussed in the draft Item) and std::swap (also discussed in the Item), do you know of examples where libraries query the noexcept status of a function?

Billy O'Neal said...

I would also add that you want to mark destructors noexcept. Throwing exceptions in destructors is almost always bad (so you can mark almost all of them noexcept). Even in cases where there is some fatal behavior that can occur in a destructor, often the guaranteed program termination characteristic of marking the destructor noexcept is superior to the potential program termination that occurs if the exception occurs during stack unwinding. (The stack unwinding behavior can appear nondeterministic)

Scott Meyers said...

@Billy O'Neal: Destructors are noexcept by default. In concept, I should address that in this Item, but that would require spending more time on noexcept expressions, which I'd prefer not to do. I'll have to see what I can do about this, because your point is certainly valid.

Billy O'Neal said...

@Scott Meyers: Interesting -- learn something new every day :)

I'm surprised that the committee made this change -- it changes the behavior of unmodified C++03 code.

Anonymous said...

My opinion, entirely on stylistic issues.
a) The exposition needs to be tightened considerably, especially if you're limiting yourself to 300 pages.
b) That few functions are candidates for noexcept should be more brutally hammered in. For one, no function that uses third-party code (modulo standard library code?) can be safely marked as noexcept.
c) That the stack might not be unwound if there's an exception thrown should be shocking to developers since it breaks a major promise of the exception-handling paradigm. Yes, I understand: don't mark your function as noexcept unless you're really sure what you're doing. However, I can't help but feel that when there's a chink (or gash) in a paradigm books like yours should really emphasize the implications.
d) I found the comparisons between C++98 and C++11 distracting. (Mr. Sutter tends to do this, too.) It might be deemed as necessary to warn practicing C++ developers that there's a major change, but surely there's an alternative way to underscore that there are alternatives in the language; after all, most (virtually all?) of C++98 features is still with us, even when there are C++11 features that supersede them. I think Mr. Stroustrup has found a way to do this better, maybe because he is teaching intro to programming with C++.

By the way, I love your books. I hope this one is the best yet!

Unknown said...

Found a typo in the reviewable draft: "chuck" of memory. It should probably be "chunk".

K-ballo said...

@Scott Meyers: I see you have covered the `std::vector` case in the item (I somehow missed it yesterday), which is the most interesting example in the standard of how `noexcept` is about semantics and not optimizations.

In the end, presenting `noexcept` as helping the compiler generate better code is like presenting move semantics as avoiding copies. Those are some of the effects of using those tools, and sure faster code will appeal to any C++ programmer, those programmers will hopefully know to not bother with that kind of optimization unless measurement supports it.

`noexcept` is part of the interface and it's a contract with the world (not just the compiler, certainly more than just a hint). It is a fundamental property of destructors, enough to guarantee breaking changes in the language, and of some basic operations to the point libraries including the standard one will pick or avoid your code based on it.

Scott Meyers said...

@Anonymous: Thanks for your comments. I'd be grateful if you could give a couple of examples of how I can tighten up my exposition, because this Item, like many in the current draft, is longer than I'd like. Regarding the stack only possibly being unwound, bear in mind that a violated noexcept means that the program will terminate; there is no way to prevent that. As such, this is not really any different from C++98's behavior when an exception wasn't caught at all. Under those conditions, the program terminated, and there was no guarantee that the stack had been unwound. As regards comparing C++98 and C++11, I should perhaps have mentioned that this Item will be in a chapter called "From C++98 to C++11." It specifically focuses on C++98 practices that need to be revised for C++11, and in that context, I think it makes sense to discuss changes between the language versions. But in accord with your suggestion to tighten up my writing, I'd be interested to hear what information you think could be condensed or omitted.

Scott Meyers said...

@Ralph Tandetzky: Typo fixed, thanks!

Anonymous said...

"22 As the comment indicates, the code is incorrect. The problem is that *src is an
23 lvalue, so this statement would unconditionally copy *src to *dest. That’d have
24 been fine in C++98, but in C++11, we want to do a move if we can."

I think "incorrect" is too strong here. How about "suboptimal"?

Anonymous said...

Maybe you should stress more that unlike, for instance, const, noexcept is never checked by the compiler. Just in case somebody thinks that explicit throw is okay but maybe calling other functions that are not marked noexcept would be caught by the compiler?

Scott Meyers said...

@bartoszmilewski.com: Regarding page 5, line 22, the context of the discussion is std::vector::push_back in C++11, and that function must perform a move if it can. In that respect, the code is incorrect. Furthermore, I hope it's clear that the broader discussion is about replacing copy operations with moves when that's possible, and any code that performs unconditional copies will fail that test. So I think it's reasonable to describe that code as incorrect. (It would also fail on move-only types.)

Regarding the fact compilers don't statically check noexcept declarations, you don't think the text on page 2, lines 1-2, says that pretty clearly?

Anonymous said...

@scott: As you say, the rules of noexcept may look absurd (they do to me), so it's really hard to reason about them. You're asking the reader to make logical deductions from a crazy premise.

Unknown said...

Unless it is stated somewhere else, I would add the rules under which a defaulted constructor/operator will be noexcept. It is somethings that i don't know for sure myself. And also specify if the rule are different between defaulted and compiler generated.

Scott Meyers said...

@Emmanuel Thivierge: I believe the only functions that can be implicitly noexcept are destructors and operator deletes. For all other functions, as far as I know, if you want them to be noexcept, you must declare them that way yourself.