My point is, C doesn't give you enough tools to build an elegant, easy-to-use interface, so people reimplement things over and over again. So in typical application code you see people using the "interface" in C++ but manually inlining the "implementation" in C. And what's why it is a fair comparison.
Generic collections in C implemented with macros are so heinous, I think this comparison may actually be fairer to C. I say that as an abuser of C macros, and someone who genuinely likes C.
Not a huge fan of those kind of macros myself, because they still leave too much of the implementation exposed. The other alternative is pointer-arithmetic implementations, which do hide the implementation better but often come with limitations (e.g. list members have to be first and you can't be on two lists at once). But either way, that's C for you. The language has its limitations, and there's only so much you can do. Comparing that to an API in a language with better support for such things seems a bit unfair and snobbish. It's like comparing a matrix-math API in a language where it's native to the guaranteed-horrific internals of a C++ library to do the same thing.
For double-linked list, a better choice is to use intrusive data structures. Cleaner and more flexible than both macros and void pointers. However, only double-linked list and binary search trees, to a lesser extend, can be implemented with intrusive data structures.
I agree that intrusive data structures are often a better choice and do not have the issues GP mentions (by using something like a container_of() macro, so the data structure member does not need to be the first member, and the object can be in multiple data structures at once). But of course many other "pointer-based" data structures such as singly-linked lists, separate chaining hash tables, etc can also be implemented with them.