Various qsort_r declarations on Mac and Linux

Let's look at the qsort_r function in Linux ( /usr/include/stdlib.h ):

 typedef int (*__compar_d_fn_t)(const void *, const void *, void *); extern void qsort_r (void *__base, size_t __nmemb, size_t __size, __compar_d_fn_t __compar, void *__arg) __nonnull ((1, 4)); 

Let's look at the qsort_r function on a Mac ( /usr/include/stdlib.h ):

 void qsort_r(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *)); 

As you can see, these declarations differ from each other (sequence of arguments). It's amazing! Would it be effective to complain somewhere to solve this problem?

+7
source share
2 answers

Would it be more effective to complain somewhere to solve this problem?

Unfortunately no. This has been too long, and there is too much code relying on it.

I think the main question is: “Why do these incompatibilities occur”? I will answer that. It seems to boil down to the fact that BSD implements it first, but with a poor interface. ISO and later GNU fixed the interface and decided that compatibility breakdown was worth it. And Microsoft is doing everything they want.

As pointed out by @Downvoter (distinguished name), qsort_r is a non-standard function. It would be nice if it were standard, but you cannot rely on it. qsort_s is a kind of standard in Appendix C11 of Appendix K, but no one implements C11, let alone its applications, and whether application K is suitable is a good idea .

Like many C and Unix issues, it boils down to BSD vs GNU vs Microsoft and their inability to coordinate C extensions. Linux is GNU. OS X is the target of many things, but for C it follows BSD.

FreeBSD added qsort_r in September 2002. Visual Studio 2005 had a slightly different qsort_s . In 2007, ISO officially changed qsort_s again. Finally, GNU appeared years later in glibc 2.8 in 2008, apparently following the ISO. It uses the old thread, covering the period from 2004 to 2008, requesting qsort_r in glibc , which has some justification.

To remind everyone, here is qsort as defined in C99.

 void qsort( void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) ); 

FreeBSD was the first in September 2002. They decided that qsort_r should break the qsort interface and put the argument "thunk" in front of the comparison function.

 void qsort_r( void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *) ); 

Why? You should ask Garrett Wollman , who wrote the patch. Looking at the patch , you can see that from its changes to the CMP , it was decided that having thunk was a good template at first. Perhaps they decided that the "comparison function comes at the end" is what people will remember. Unfortunately, this means that the qsort and qsort_r comparison functions change their arguments. Very confusing.


Meanwhile, Microsoft, an ever innovator, qsort_s in Visual Studio 2005 .

 void qsort_s( void *base, size_t num, size_t width, int (__cdecl *compare )(void *, const void *, const void *), void * context ); 

"s" for "safe", not "r" for "reuse", which everyone else used, perhaps in accordance with the ISO agreement (see below) or vice versa. They put "thunk" at the end of qsort_s , keeping the arguments the same as qsort , but for maximum confusion, "thunk" comes at the beginning of a comparison function such as BSD. They chose the worst possible option.


To make matters worse, ISO published TR 24731-1 in 2007 to add border checking to the C standard library (thanks @JonathanLeffler for pointing this out). And yes, they have their own qsort_r , but it's called qsort_s ! And yes, this is different from everyone else!

 errno_t qsort_s( void *base, rsize_t nmemb, rsize_t size, int (*compar)(const void *x, const void *y, void *context), void *context ); 

They wisely decided to keep qsort_s arguments, and its comparison function is a superset of qsort , probably arguing that it would be easier for people to remember. And they added the return value, probably a good idea. To add to the confusion, at that time it was a “Technical Report” and not part of Standard C. Now this is “Appendix K” of standard C11, still optional, but has more weight.


GNU solved the same, perhaps the following ISO qsort_s .

 void qsort_r( void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg ); 

Looking at the glibc update by adding qsort_r was probably also easier to implement. To know for sure, you have to ask Ulrich Drapper.


The BSD decision to exchange arguments using qsort and its comparison function probably caused a lot of confusion and error over the years. ISO / GNU's decision to keep them the same, perhaps better. ISO decided to call it another name. GNU decided to break BSD compatibility. Microsoft decided to do anything. Now we are stuck with four incompatible implementations. Since the comparison functions have different signatures, the compatibility macro is nontrivial.

(This is just a reconstruction of the code. For their actual justification, you will have to break through the mailing list archives.)

I can’t blame GNU or BSD or ISO or Microsoft ... well, I can blame Microsoft for killing C. Point is the process of standardizing C and expanding this standard, as well as getting compilers, this standard is painfully slow, and compiler authors sometimes should do what is appropriate.

+14
source

As written here , qsort standardized (C99), but qsort_r is a GNU extension (" qsort_r() was added to glibc in version 2.8"). Thus, there are no requirements for them to be the same on different platforms, not to mention portability.

+2
source

All Articles