7 ms·
>is not expected to need to be thread safe
I'm not "expecting" his example code to be multithread safe either. (By default, I expect all code to not be multit
>is not expected to need to be thread safe
I'm not "expecting" his example code to be multithread safe either. (By default, I expect all code to not be multithread safe unless it is explicitly engineered to be so.)
I'm pointing out that the code does not work the same way under the covers. The author would know there's mutable state hidden in that "list" object but I don't think it would be obvious to others at first glance. If it was, programmers wouldn't get bitten by bugs because of misunderstanding ctime() and strtok() in single-threaded programs. Likewise, the zlist_next() can also be inadvertently misused to cause bugs in single-threaded programs.
If the author knows how czmq internally works and how it's supposed to be used (don't multi-thread or misuse in single-thread), but outsiders haven't grokked those guidelines yet, can that coding paradigm be labeled as a "dialect"? That's the interesting philosophical question to me. Words like "dialect", "explicit", "syntactic tricks" are relative to the programmer and I think the author's code examples illustrate those subjective biases rather than advise us of objective truth.
I think there is a fundamental difference there in that as you say: You by default expect all code to not be multithread safe. That's a default assumption for most C/C++ developers when encountering a class or library they don't know because it is very common. To the extent that quite a few people argue that libraries should not take extra effort to be thread safe, as is the case here. Besides, it is obvious from the API in this case that there is hidden state to all but extreme beginners.
I think his argument regarding dialects in C++ is sound to an extent. C is quite boring and settled when it comes to structure and interop at the API level. APIs may vary wildly, but e.g. <libraryidentifier>_<classname>_<methodname> or <libraryidentifier>_<functionname> with pointer to an "object" or struct holding generic state is very well established, for example. A lot of the simplicity there comes from the simplicity of the language. The internals of how the code is written varies more, and things like build systems etc. varies wildly. But at the end of it you are usually left with some headers and a bunch of exposed symbols that follows a relatively limited set of styles.
C++ has far trickier issues, in that you are dealing with people who wrap everything in templates; people who try to use functors all over the place; people who want exception safety everywhere; people who ban exceptions in their codebases; smart pointers vs. bare pointers etc. C library code can usually interoperate. C++ code written in different "cultures" can sometimes be like trying to make LEGO and PlayMobil fit toys fit together. Many C++ developers have gone through "phases" - for my own part, before I mostly ditched C++ I'd written codes in styles that appeared totally alien to each other...
Exactly.
I would add that this goes deeper than just coding styles. My current project relies on a number of widely-used C++ libraries (Qt5, OpenCV, ITK, VTK and a homegrown framework) and each one of them redefines basic data types (std::string, QString, cv::String).
I could go on about how none of them even acknowledges the existence of <cstdint>. I'm sure there are good excuses why each individual library decides to do this (lack of common ABI, badly designed std::string), but the end result is a heap of forced runtime - and cognitive - overhead.
This is pretty sad considering that C++ is advertised as a language that doesn't add unnecessary runtime overhead.
A lot of it is simply age.
Qt and VTK predates C++98 significantly. OpenCV is slightly newer, but it took a long time for people to get comfortable relying on it, and I don't know whether or not it was based on older, pre-existing code.
But it's probably going to remain a drag on C++ for another couple of decades...
On the other hand, std::string already existed back then, but they still chose to reimplement it.
This points to a more systemic problem in the C++ ecosystem. Has C++ evolved to the point where, say, a new library written today would not have to redefine std::string?
It existed, but if you wanted interoperate with your older code, or wanted to run on various platforms, it often would not be available, or bugfree enough or fast enough. It was well after 2000 before everyone started trusting that they'd have a reasonably conformant C++98 environment. You'd still find people start new std::string alternatives up until then, for seemingly good reasons. Some of them valid, some of hem not so much.
Today I don't think anyone would redefine std::string, but I haven't written much C++ for years.
The standard was finalized in 1998 but the implementations on a wide variety of necessary platforms didn't happen until later. Qt's QString predated 1998.
Also, this SO q&a has more tidbits: http://stackoverflow.com/questions/1618798/why-is-there-a-different-string-class-in-every-c-platform-out-there http://stackoverflow.com/questions/1618798/why-is-there-a-di...
Not it didn't. The work on Qt standard on 1991, according to Wikipedia. The work on STL only started in 1994 and became a standard with C++98, and even then it took time for compilers to get decent standard support. So every library needed its own string implementation. New libraries generally use C++ string, unless they're have very strong opinions about performance that trump interoperability (e.g. Facebook's folly).
But seriously, isn't that hypocrite to blame old C++ libraries for implementing their own string class, and then go espousing C? C of all languages - the language whose spartan standard library includes almost nothing (including usable string processing code, if you care even slightly about securing your code from memory errors), and each respectable C project ends up implementing their own lists, queues, maps, stacks and of course strings.
No, it isn't "simply age." I know projects started in 2014 that defined their own string types since there are a lot of things that std::string doesn't support. For example, you can't turn a C string into a std::string without copying. There is basically no usable unicode support in std::string. Copies of std::string use copy-on-write, which may create hidden race conditions when sharing data between threads. If you want to fix those problems, you need your own string type.
How does std::string copy on write cause race conditions?
Because if you "copy" a string and the copy really is just a reference to some shared data structure, changing the shared data structure later in another thread without memory barriers or other synchronization causes undefined behavior. Remember that the shared data structure will need a reference count to be updated whenever a reference gets deleted.
Two things to note. std::string is pretty much barred from being CoW as of C++11. QString is CoW, but as of Qt4, cross-thread copies are safe to modify just like normal objects.
> you can't turn a C string into a std::string without copying.
C and C++ use value semantics by default. std::string is a value., a char* is a reference, so this shouldn't be surprising. If you want an immutable, zero-copy, drop-in replacement for std::string, use boost::string_ref (a variant of which will soon be standard). It'll give you a memory unsafe (just like a char\) reference to a C string or* a std::string. Want to wrap a C string safely? Use it in combination with unique_ptr or shared_ptr<char> (for exception safety).
> Copies of std::string use copy-on-write,
Actually that's explicitly forbidden by the standard, since C++11. The interface for std::string always assumed mutation, which made COW really inefficient.
> which may create hidden race conditions when sharing data between threads
The COW versions of std::string, that I'm aware of at least, were always thread-safe.
I'm curious why std::string is badly designed?
I'm also curious why. I wonder if any of the original designers ever shared any insight into their design process.
As for what is wrong with it, multiple things. My personal pet peeves are:
1. the lack of formatting or interpolation support (i.e. QString::number(3.14, 'f', 2) or QString("Hello, %d!").arg("world")
2. the utter lack of Unicode support
I've seen other people complain about how it interacts with <algorithm>, inefficient support for common operations (join, split, copy) and mutability. I don't particularly care about these, given that its lack of Unicode support pretty much relegates it to a glorified std::vector<char> implementation for me.
Yes, you can read "The Design and Evolution of C++" and the "The Annotated C++ Reference Manual" aka C++ ARM.
There you will understand why C++ is the way it is, when you are trying to sell a safer language with higher level abstractions to hardcore C developers at AT&T, with zero friction with existing C toolchains.
Many like me hate the C underpinnings of C++, but if it wasn't for them, most likely no C vendor would ever have adopted it.
I'm tired of people blaming the bad things in C++ on C. Most of the bad things in C++ came from C++ itself, not from anywhere else. The Modula-3 type system with its inheritance anti-patterns can't be found anywhere in C. "Guru meditation" template error messages can't be found anywhere in C, nor do C programs typically take hours to compile. In C, functions and types that are "static" to a file don't appear in header files... C++ "improved" that so that private types and functions must appear in header files, greatly extending compile times. Global constructors are not in C... you know what order your program will run in when using C.
The set of things in C that were "fixed" by C++ is very small, possibly even the empty set for reasonably modern dialects of C. For example, having the compiler implicitly declare functions as returning "int" was on its way out with or without C++.
Probably the worst thing about C was all the implicit type conversions-- between long and int, and char* and boolean. Of course C++ didn't fix this. They even made it worse by declaring that any C++ constructor that took one argument would be a candidate for implicit type conversion. So if you declared Foo::Foo(int i), in the future any time you have an int the compiler would consider promoting it to a Foo. Later they realized their mistake and added the "explicit" keyword so C++ gurus can avoid this error most of the time (when they remember).
It seems you are blaming C++ as a language for the design of the libraries you are using.
I've also never seen a program actually be slow due to string manipulation (excluding grep). My guess is that if you profile, it will turn out that your performance is getting away from you in places you don't expect.
> It seems you are blaming C++ as a language for the design of the libraries you are using.
Precisely. In the same way that Java is criticized for proliferating the IAbstractFactoryVisitor design anti-pattern, C++ should be criticized for its weak standard library which causes an overproliferation of mutually-incompatible reimplementations of basic functionality.
As to whether this is or is not a performance issue depends on the specifics of the application and the underlying hardware.
This is besides the point to this discussion. Why don't C# libraries reimplement System.String like C++ libraries reimplement std::string? That's the real question here.
Because when C++ appeared, its standard library was basically C.
Then you had the early adopters that were basically the same developers that were still writing C with lots of inline Assembly.
So each developer, or team, had to write their own library, because they knew better.
It took all the way up to ANSI C++ (1998) to finally have a proper minimal library. Minimal, because C++ needed to cater to the C mentality that the OS is the runtime.
Then you had almost a decade until the C++ vendors were able to catch up the standard.
Mind you that in the mid-90's there were still quite a few C compilers that were K&R only.
This is why when Java appeared, many of us C++ devs that cared also about safety, jumped into it. As it provided a more portable experience.
Modern libraries don't reimplement std::string. The C++11-14 standard library is actually pretty solid.
I was actually curious if this is true, and it appears that Unreal Engine 4 reimplements std::string (FString) [1] I think this qualifies as a modern library.
My yardstick for "pretty solid" is actually meaningful Unicode support, so I will reserve judgement as to whether the C++11 STL qualifies.
[1] https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/StringHandling/index.html https://docs.unrealengine.com/latest/INT/Programming/UnrealA...
I agree with you that C++ streams are rather horrible. This is probably the oldest part of the standard library and the original design predated both templates and exceptions, so it had to do with virtual functions and error states. I wish we could have better library in the standard. If I needed performance, I would rather use a third party I/O library over stdio, which is a security nightmare.
> There isn't a single "modernist" style in C++, there are dozens of permutations
All complex languages have several styles, so it's not just a feature of C. Take a look at Scala (mutable vs immutable objects), Haskell (recursion vs. folding, lambdas vs. point-free) or even Javascript (callback-hell vs. promises vs. generators vs. async/await) and it's not so different than C++. If you want one style to rule them all, there's always Go.
> More than half of the really difficult problems we encountered were C++-only, relating to constructs that didn't exist or were simpler in C.
Yes, constructs like classes or templates don't exist in C, but their warts are generally born out of the requirement for C compatibility.
Excruciating long compile time? Due to compatibility with C's preprocessor-based "module system".
Private variables and methods have to appear in header files? Again, wouldn't be an issue if not for the mandated module system inherited from C.
Weird template errors? Compatibility with C types made introducing constraints is quite hard (that's one of the reasons Concepts are languishing in the standards committee I guess).
> And some of the most exploitable software out there like Adobe Flash is written in the oh-so-safe C++ rather than the evil old C.
This software is more likely written in C with a sprinkle of C++ classes, lots of manual memory managements and, raw C arrays, C library calls and so on. My experience might not be representative enough, but the developers who tend to write this kind of malloc-happy code are the same developers who hate templates, exceptions and RAAI. You might as well call them C developers in a sheepskin.
> I agree with you that C++ streams are rather horrible.
Apparently I belong to that strange group of C++ devs that likes iostreams. :)
I never said C++ streams were "horrible", just that they were slower than stdio and still got adopted. Which kind of negates the idea that among C++ "designers" (I use that term loosely) that you have to make all sorts of terrible semantic compromises to save a tiny bit of performance.
I don't think stdio is "a security nightmare" either (I assume this canard is based on the fact that it still technically includes "gets" or some other such function that nobody has actually used in new code in 3 decades.)
C++ is a uniquely schizophrenic language because you can't even use libraries developed in one style with code developed in another style. Try throwing an exception when -fno-exceptions is enabled in a google code base. Try passing code using a QString to code that wants a std::wstring. Tee hee. You can't say this about Javascript, Haskell, etc. and frankly your comparisons are vapid.
And the C++ sheep keep bleating "it's C's fault, baaaaa" even after I gave the example of Objective C, which has modules and avoids C++ template hell. You even non-ironically blamed C for the C++ concepts feature "languishing in the standards committee." Can you not see that this is the equivalent of "thanks, Obama" whenever something goes wrong halfway across the world? What the hell does C have to do with templates or concepts?
Yes, I'm sure evil C programmers are responsible for all crappy and insecure C++ software. They're in league with the Zionist media conspiracy to smear C++'s good name. We must root out these double agents wherever we find them! Onward C++ soldiers. Never question the decisions of your leaders, which are made for excellent reasons and always make complete sense.
> For example, Objective C is a better language than C++, also built on top of C.
Somehow I cannot agree that a language that has [] and @ everywhere, relies on the programmer to manually write retain/release, uses dynamic typing and keeps all C defects, has a better design.
Remember Objective-C didn't had ARC in 1983.
> But I guess the evil C designers somehow forced them to do that? At what point do you start taking responsibility for your own mistakes? It's sounding a lot like "never".
Yes, when 100% compatibility with existing code is a design goal.
> Objective-C does, and people are talking about adding one to C++.
Objective-C modules is a work from Apple, relies on clang linker and wasn't part of the original language design.
Likewise, the upcoming C++ modules require the implementation of new linkers.
Both don't work with the UNIX linker model that Bjarne had to use.
Just the C++ compiler was new. The linker had to be the system linker already in place. This is how name mangling came to exist.
Languages that use modules and have their own linkers don't rely on name mangling.