I really enjoyed your May 2012 post "Why I should have written ZeroMQ in C, not C++ (part I)".
I agree with your assessment of C++ exceptions. Many commentors don't quite get what you did not explicitly state: a far-away error-handling method designed to handle multiple exception types for convenience is unlikely to be able to handle errors in a fully recoverable way. Exceptions are designed to decouple error handling away from the immediate caller, which is the only context that truly understands the API and the state it is dealing with. An exception is a goto to a (statically) unknown destination at the worst possible time (a crisis moment).
Good points have been made that C-style error handling:
1. can be ignored without run-time error
2. take up your only function return value.
Newer languages seem to be eschewing exceptions in favor of a more C-like approach that solves the above two issues based upon tuple-like return types ("value or nothing", or "value or error code") that have to be decomposed and checked for error explicitly to receive return values. This also allows the compiler to guarantee that an error code is checked, rather than relying on run-time detection. Progress is being made, but too late for C++.
My perspective is that exceptions were designed to solve a local problem (consolidated error handling) in a way that introduces a bigger problem "in the large" (on the scale of the project, rather than the function). In fact, I find that a few C++ features (especially the early ones) make this kind of bad trade-off.
Interestingly, I was recently developing an in-memory database as a library (in support of a game) and I began by using plain C. There were many good reasons in my mind to do so:
1. I would very much like to avoid exceptions and OOP (inheritance, virtual functions), probably more "features" as well
2. A C API can be used from C, C++, or scripting languages (C++ has no standard name-mangling, making linking to C++ generally unsupportable)
3. A plain C interface eliminates library-seeping-into-interface problems with boost types, smart pointer types, etc that cause version/linking issues for the main application (which may use many libraries, each of which may want different versions of the same sublibraries!)
4. probably more I don't remember
However, as the project evolved, I found *some* C++ features to be very desirable, so the project evolved to a subset of C++. The main subset I use are:
1. function overloading (application changes a type? they don't have to change all function calls in project)
2. generic programming (via templates): eliminates code duplication, works well with #1. makes for smaller code base
3. C cannot do callbacks performantly or well (lambdas via #2)
Note that I still do memory management C-style (no constructors, destructors, or methods: plain functions only). This has better encapsulation properties than C++ methods-in-a-class approach, which must declare its internally-used data types "publicly" in the header file! (& therefore must also include all headers needed to describe those types: the difference in header file size is striking)
Of course I also use C-style error handling. The API is technically C++ (for function overloading), but is kept to a plain, no-library style.
So question, Martin: did you ever consider (for nanomsg, for example) using C++ but simply limiting your feature use to a small, useful subset? Do you not find any features of C++ compelling enough?