I don't buy the `it's better because it's C rather than C++`. For example, "Number of memory allocations is drastically reduced as intrusive containers are used instead of C++ STL containers." can be solved in 3 ways:
1. use an intrusive container in C++.
2. Just use a `vector`, faster in most cases, regardless of complexity.
2. Prevent users from having to use Boost or the rest of the C++ nonsense
3. Users can now write trivial language bindings and link at runtime easily.
4. Profit.
~
C++ is a cool bro and all, but honestly the solutions you've listed make it annoying to work with this code outside of C++ on a platform with subgood template support.
Moving a simple utility library like ZMQ to C makes a great deal of sense.
Eventhough the backend for ZeroMQ was written in C++, the front-end, and the main language bindings as it were, are in C. That means it was already trivial to write language bindings.
No, the ZeroMQ author should be writing idiomatic code in whatever language he is using.
In his blog post, he complains that std::list<widget* > causes 2 allocations per insertion.
Fine, because that's not idiomatic C++. The C++ way would be to use std::list<widget>, since there's no need for the extra indirection.
Note that this would only be a good choice if you for some reason really need a list, std::vector is mostly preferred due to its better cache behaviour and requires even fewer allocations.
Secondly, if he needs to remove list items by their address, there's no reason he couldn't use an intrusive list in C++. The article I linked shows an implementation of one, and there's always boost::intrusive::list if you don't want to roll your own (and you shouldn't).
Extra indirection is needed when the objects are non-assignable, e.g. if there's a thread running inside the object, if it owns a fd or similar. std::vector has O(n) complexity for a lot of operations, so it's not an option.
If you can use C++11, this becomes a non-issue with move semantics.
Ownership of internal resources can be transferred, and emplace_back even allows constructing your object in place.
As others said, the O(n) really becomes more like pseudo-O(1) due to cache effects unless you have elements the size of your cache lines (in which case prefetching still helps) or you're only fetching one element at a time and then triggering a context switch (as in a scheduler).
I'm not bashing your library, and if you prefer to use C then that's great, but it's kind of unfair to blame it on the language in the first place.
Well, this is just precious. When the first C++ recommendation turned out to be not applicable, the answer is:
1) He can solve the issue by requiring C++11 (!!)
2) Just rely on cache-effects making it pseudo-O(1), unless
(insert various cases where it might blow).
Sure, he can jump through 200 hoops to use C++, special features, rely on CPU cache behavior, finetune to death, etc -- or, you know, he can use C and be done with it...
Languages are designed for specific purposes. I am claiming that C++ is not the best language for system development. The fact that most OSes are not written in C++ is a good indication of the fact. Still, C++ is great for rapid development & corporate development.
OS X drivers are "a restricted subset of C++"[1] that restricts you from using Exceptions, Multiple inheritance, Templates, and Runtime type information (RTTI). Also, not all drivers use IOKit.
That is one of the problems with C++. In "C" land you also might be put to restrictions - for example don't use malloc()/realloc()/free() but some other functions, or don't use the provided assert() but something else, or no Xxxprintf() functions, etc - e.g. most of them are around function calls, that could be caught at preprocessor, compiling, or even runtime level.
Now C++ comes with new/delete, dynamic_cast<>, throw/catch/etc, RAII, always export "C" interface (to avoid mangling) - While all of these are useful, due to the constraint where the language has to be used they can be forbidden. It's no longer functions that you are restricting from being called (as in "C"), you are forbidding to use parts of the language that are considered by many as vital, and it's basically a different language at this point.
I'd like to know what restrictions cause you not to be able to use RAII.
Even if that's the only C++ feature that a project were to use, I'd still consider it a net gain. Anything that makes your program less susceptible to manual resource management bugs in "goto cleanup"-style C is worth using. This doesn't just include memory leaks, but more insidious problems like unclear ownership of various system resources (sockets, file handles...). Things not even a GC would help you with in a long-running server process.
There are several popular "C" libraries that use them - jpeglib comes to mind, but there are others. Also certain language's runtime environment uses it. There is a way to handle it, but you need to take care.
Even in C, setjmp/longjmp leave all non-volatile automatic storage duration variables on the current stack frame in an indeterminate state if they are modified between the two calls.
To get defined behaviour with regards to destructors, you simply have to make sure that the function calling setjmp doesn't internally use RAII. Call a wrapper function to do the setjmp and you're safe.
Of course it's a whole other question whether using setjmp/longjmp to implement "exception handling in C" is a good idea in the first place.
Unfortunately, the complexity argument is generally bullshit and you really need to profile. It turns out multiple very respected authors have found under a typical work load, `vector` performs very well on a lot of machines.
Since you never traverse the list, the order of elements obviously doesn't matter. In that case, you can just swap the element to remove with the last element.
A vector will still give you better cache-locality and also amortize the number of allocations.
Yes, that's exaclty what ZeroMQ is doing. Then check how the code looks like. It's a mess. Intrusive containers deliver same performance characteristics and the code actually looks clean.
Nothing. But if he is going to write C in C++, then what is the point of writing C++? Especially sticking to C gives substantially increased flexibility in terms of language bindings, embedded usage etc.
I've probably spent more years writing C++ code than C code, but I'm all with him on this - if what you need to do for whatever reason don't need/benefit all that much from C++ when accounting for all constraints, then choosing C as being the lowest common denominator between other languages is a very good choice for a library.
Yes, the world does not end. For small objects and `vector`s, it's probably quicker, because 1) you found it quicker due to cache locality and 2) there were no system calls to release memory.
So, what you say is:
I don't care to implement an efficient solution, because the CPU cache will fix it anyway.
That way of thinking is well known as "The Java way of problem solving" (TM)
That's not what he said, and his solution is not "inefficient." CPUs have no emotions; if it runs faster due to cache, it's simply the better solution.
For the use case required by ZeroMQ, it's faster in all cases, because swap-with-last and shrink is O(1). Sure, if CPUs didn't have caches, a list might be faster.
Do you see where this is going? The C way of solving problems consists of adding one more level of indirection (ever heard of 3-star C programmers?), because it becomes more efficient asymptotically that way. Nevermind the hidden factor of 100-1000 due to pipeline stalls and cache misses.
Even the code in the article makes heavy use of C++ features (templates, resource management) and that's not even talking about the Boost intrusive library that gets mentioned, which is knee-deep in template effluvial...
Never mind that I've seen plenty of C code with wrapping structs for data structures, too. Intrusive vs. non-intrusive is a design choice in both languages.
The point of intrusive lists is to allow access to the link when you provide only the contained object.
The way you're thinking of would theoretically work, but you'd have to make sure the compiler isn't adding fancy padding that would break your pointer manipulation and type casting.
boost::intrusive::list on the other hand, works in a type-safe manner by having your class either inherit a list hook, or provide it as member and then pass the list implementation a member pointer as template parameter so that it can be accessed.
That's one way to look at it, but certainly not the only one. Back in 2007, when he started ZeroMQ, you couldn't build on C++11 features. Furthermore, if you're targeting slightly less common platforms, then C++ runtime support and recent C++ features is always a hit-or-miss issue.
For a library that is at such a basic level of infrastructure (which, if it's stable enough and popular enough deserves to be kernel-level) C++ is probably more trouble than it's worth.
The higher you go app the stack, the more reasonable it becomes to go farther from C (high enough, and even ruby makes sense - though it wouldn't for 0mq/nanomsg)
In C++ it's even easier than in C to safely use stack-based allocation (thanks to RAII) so this it's better because it's C rather than C++ is a huge warning sign.
The purpose of C++ is to provide experts with a toolbox to do anything without any compromise. This is why the language is hard to master and very easy to misuse. It's a two edged sword literraly. Although in C++ one could say it's more like a two-edged chainsaw.
In this case, I would simply say that the author problem is more about how he relates to C++ than about C++ itself.
Saying C++ is abrasive is a valid point, but saying it cannot be as fast as C or it cannot do "something" is stratospherically stupid.
The original post only claims that it makes his life easier, and then makes two further claims about the C implementation without stating you can't achieve the same in C++.
He then links to a post where he goes into more detail, including _explicitly pointing out_ you can do the same in C++, but that he believes that the language encourages you to think in certain ways that negatively affects these factors.
It seems to me that he is stating his reasons for why he believes C is the best choice, and particularly for him, without making any claim that it is objectively better.
In other words, unless I've completely missed something, you are setting up strawmen.
I was referring to his original rant on C++: http://250bpm.com/blog:4 and http://250bpm.com/blog:8. They are pretty misinformed. He claims in C++ the erasing solution is O(n) where it is O(1) in C. This doesn't make any sense of course.
The comments you are referring to where he moderates his position are resulting from the comments he received from the C++ community regarding his incorrect statements.
But then if you have some 3rd party library code using setjmp/longjmp (libjpeg and others) this would no long work with RAII - no unwinding would be done. That is if the your library calls a "callback" which calls one of the said 3rd party libs (or your code) with longjmp
i'm pretty sure the real reason for C is that the author is just more familiar with it (which is a fine point imo)
now there's other things i'd criticize, like nanomsg is a trademark of the company.. so if you repackage it i'm suspecting you have to rename all the stuff, for example.
Why would you be pretty sure he is more familiar with C when most of his past projects were written in C++? Not to mention that he's written two blog posts explaining his reasoning...
I'm not sure the trademark is a big deal, it's probably to prevent misuse like what happened with the Linux trademark (which took a few years to resolve). I doubt he's doing it to make it more annoying to use.
1. use an intrusive container in C++.
2. Just use a `vector`, faster in most cases, regardless of complexity.
3. Read about and use allocators properly.