Sarà efficace lamentarsi da qualche parte per risolvere questo problema?
Ahimè, no. È stato così per troppo tempo e c'è troppo codice che fa affidamento su di esso.
Penso che la domanda di fondo sia "perché si verificano queste incompatibilità "? Risponderò a questo. Sembra ridursi a BSD che lo ha implementato prima, ma con un'interfaccia scadente. ISO e successivamente GNU hanno sistemato l'interfaccia e hanno deciso che ne valeva la pena. E Microsoft fa quello che vuole.
Come sottolineato da @Downvoter (grande nome), qsort_r
è una funzione non standard. Sarebbe bello se fosse standard, ma non puoi fare affidamento su quello. qsort_s
è una specie di standard nell'allegato K C11, ma nessuno implementa davvero C11, per non parlare dei suoi allegati, e se l'allegato K sia una buona idea è in discussione.
Come molti problemi di C e Unix, questo dipende da BSD vs GNU vs Microsoft e dalla loro incapacità di coordinare le estensioni C. Linux è GNU. OS X è un miscuglio di molte cose, ma per C segue BSD.
FreeBSD ha aggiunto qsort_r
nel settembre 2002. Visual Studio 2005 presentava un qsort_s
leggermente diverso . L'ISO ha formalizzato ancora una volta un diverso qsort_s
nel 2007. Finalmente GNU è arrivato anni dopo in glibc 2.8 nel 2008, apparentemente dopo l'ISO. Ecco un vecchio thread dal 2004 al 2008 che richiede qsort_r
essere implementato in glibc che ha alcune motivazioni.
Per ricordare a tutti, ecco qsort
come definito in C99.
void qsort(
void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *)
);
FreeBSD è stato il primo nel settembre 2002. Decisero che qsort_r
dovrebbe interrompere l'qsort
interface e inserire l'argomento "thunk" prima della funzione di confronto.
void qsort_r(
void *base, size_t nmemb, size_t size,
void *thunk,
int (*compar)(void *, const void *, const void *)
);
Come mai? Dovrai chiedere a Garrett Wollman chi ha scritto la patch. Guardando la patch puoi vedere dalle sue modifiche a CMP
si decise che avere prima il "thunk" era un buon modello. Forse hanno deciso che "la funzione di confronto va alla fine" era ciò che la gente avrebbe ricordato. Sfortunatamente questo significa qsort
e qsort_r
Le funzioni di confronto di hanno i loro argomenti invertiti. Molto confuso.
Nel frattempo Microsoft, sempre l'innovatore, ha 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" per "sicuro" piuttosto che "r" per "rientrante" che tutti gli altri stavano usando possibilmente seguendo una convenzione ISO (vedi sotto) o viceversa. Hanno messo il "thunk" alla fine di qsort_s
, mantenendo gli stessi argomenti di qsort
, ma per la massima confusione il "thunk" va all'inizio della funzione di confronto come BSD. Hanno scelto l'opzione peggiore possibile.
A peggiorare le cose, nel 2007 ISO ha pubblicato TR 24731-1 per aggiungere il controllo dei limiti alla libreria standard C (grazie @JonathanLeffler per averlo segnalato). E sì, hanno il loro qsort_r
, ma si chiama qsort_s
! E sì, è diverso da quello di tutti gli altri!
errno_t qsort_s(
void *base, rsize_t nmemb, rsize_t size,
int (*compar)(const void *x, const void *y, void *context),
void *context
);
Hanno saggiamente deciso di mantenere gli argomenti a qsort_s
e la sua funzione di confronto è un superinsieme di qsort
probabilmente sostenendo che sarebbe più facile da ricordare per le persone. E hanno aggiunto un valore di ritorno, probabilmente una buona idea. Per aumentare la confusione, all'epoca si trattava di un "rapporto tecnico" e non faceva parte dello standard C. Ora è "Annex K" dello standard C11, ancora opzionale ma ha più peso.
GNU ha deciso la stessa cosa, possibilmente seguendo il qsort_s
dell'ISO .
void qsort_r(
void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *, void *),
void *arg
);
Guardando la patch di glibc che aggiunge qsort_r
probabilmente era anche più facile da implementare. Per saperlo con certezza dovrai chiedere a Ulrich Drepper.
La decisione di BSD di scambiare argomenti con qsort
e la sua funzione di confronto ha probabilmente causato molta confusione e bug nel corso degli anni. La decisione ISO/GNU di mantenerli uguali è senza dubbio migliore. L'ISO ha deciso di dargli un nome diverso. GNU ha deciso di interrompere la compatibilità con la funzione BSD. Microsoft ha deciso di fare qualsiasi cosa. Ora siamo bloccati con quattro implementazioni incompatibili. Poiché le funzioni di confronto hanno firme diverse, una macro di compatibilità non è banale.
(Questa è tutta una ricostruzione del codice. Per le loro effettive motivazioni dovrai scavare negli archivi delle mailing list.)
Non posso davvero incolpare GNU o BSD o ISO o Microsoft ... ok, posso incolpare Microsoft per aver deliberatamente cercato di uccidere C. Il punto è il processo di standardizzazione di C, e l'estensione di quello standard, e convincere i compilatori a seguire quello standard è dolorosamente lento e gli autori del compilatore a volte devono fare ciò che è opportuno.
Come scritto qui, qsort
è standardizzato (C99), ma qsort_r
è un'estensione GNU ("qsort_r()
è stato aggiunto a glibc nella versione 2.8"). Quindi, non ci sono requisiti affinché sia uguale su tutte le piattaforme, figuriamoci portabile.