A quick perusal of the hashmap landscape[0] in C++ shows a vibrant ecosystem where newer and faster implementations appear every year or so.
No, it's more of a reflection of how many different ISAs and compilers there are for C.
To get a packaging system going for C you'd have to support quite a few of them and, even then, some degree of forensic analysis would have to be done for existing codebases in order to determine if they truly support what they claim to support.
For instance, in 2017 I had to modify hiredis to properly support the Qualcomm Snapdragon 800 since it had quite a few instances of type punning which were causing bus errors.
Did hiredis support this platform in 2017? Who knows?! Hell, it doesn't even mention on its GitHub which ISAs and compilers it's intended to support.
At MINIMUM, packages need something akin to the table on the following page in order to get a packaging system going for C: https://liblfds.org
--
I work with C daily. Unfortunately, I so often have to patch dependencies to work on the targets I support that I'm in the habit of "vendoring" any code I depend on. This practice, I imagine, is pretty common for multi-platform C projects.
No, it is a sad state of the ability of C to encapsulate things. You cannot define vector<T> in C. Using some macros you probably can devise a way to get an implementation of a dynamic array for your type of an element, but... macros... And in any case it probably will be ugly. If you can implement hash table in a few dozen of lines, it is much better.
It's comparing apples to oranges. If in C++ you removed allocators, exceptions, references, and generics, then unordered_map would be "quick" too.
The best technique I have seen is to have an open addressing hash table whose keys are 64 bit hashes and values are pointers.
Then you layer on top of that to make a string dictionary. The value of the hash table is a linked list chain of key value pairs where the key is a string.
In the vast majority of cases you never touch the chain. Only when two keys have identical 64 bit hashes (not bucket indices) do you need to disambiguate with string equality.
This design is much faster and can be easily reconfigured for key types which are not strings, with no changes to the underlying hash table.
I'm not sure why Chris overlooked this in writing his article. The string comparison is supposed to only happen when the hash values are equal.
Sometimes textbooks focus too much on the abstract idea, and throw out what they think are unnecessary details, when they are actually important contributions by practitioners.
In this implementation the values type is a pointer, so you still have to deref those and you get no perf benefits.
In addition you can compose the functionality of the more complex tables on the simple ones. You need custom get/set hash table probing for the key type.