Dans quelle mesure la fonction ré-entrante qsort_r est-elle portable par rapport à qsort?

qsort_r() est la version ré-entrante de qsort() qui prend un argument ‘thunk’ supplémentaire et le transmet à la fonction de comparaison. J’aimerais pouvoir l’utiliser dans du code C portable. qsort() est POSIX et partout, mais qsort_r() semble être une extension BSD. En tant que question spécifique, cela existe-t-il ou at-il un équivalent dans le runtime de Windows C?

J’ai tenté d’écrire une version portable de qsort_r / qsort_s (appelée sort_r) illustrée par un exemple. J’ai également mis ce code dans un repository Git ( https://github.com/noporpoise/sort_r )

 struct sort_r_data { void *arg; int (*compar)(const void *a1, const void *a2, void *aarg); }; int sort_r_arg_swap(void *s, const void *aa, const void *bb) { struct sort_r_data *ss = (struct sort_r_data*)s; return (ss->compar)(aa, bb, ss->arg); } void sort_r(void *base, size_t nel, size_t width, int (*compar)(const void *a1, const void *a2, void *aarg), void *arg) { #if (defined _GNU_SOURCE || defined __GNU__ || defined __linux__) qsort_r(base, nel, width, compar, arg); #elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \ defined __FREEBSD__ || defined __BSD__ || \ defined OpenBSD3_1 || defined OpenBSD3_9) struct sort_r_data tmp; tmp.arg = arg; tmp.compar = compar; qsort_r(base, nel, width, &tmp, &sort_r_arg_swap); #elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__) struct sort_r_data tmp = {arg, compar}; qsort_s(*base, nel, width, &sort_r_arg_swap, &tmp); #else #error Cannot detect operating system #endif } 

Exemple d’utilisation:

 #include  /* comparison function to sort an array of int, inverting a given region `arg` should be of type int[2], with the elements representing the start and end of the region to invert (inclusive) */ int sort_r_cmp(const void *aa, const void *bb, void *arg) { const int *a = aa, *b = bb, *p = arg; int cmp = *a - *b; int inv_start = p[0], inv_end = p[1]; char norm = (*a < inv_start || *a > inv_end || *b < inv_start || *b > inv_end); return norm ? cmp : -cmp; } int main() { /* sort 1..19, 30..20, 30..100 */ int arr[18] = {1, 5, 28, 4, 3, 2, 10, 20, 18, 25, 21, 29, 34, 35, 14, 100, 27, 19}; /* Region to invert: 20-30 (inclusive) */ int p[] = {20, 30}; sort_r(arr, 18, sizeof(int), sort_r_cmp, p); int i; for(i = 0; i < 18; i++) printf(" %i", arr[i]); printf("\n"); } 

Comstackr / exécuter / afficher:

 $ gcc -Wall -Wextra -pedantic -o sort_r sort_r.c $ ./sort_r 1 2 3 4 5 10 14 18 19 29 28 27 25 21 20 34 35 100 

J'ai testé sur Mac et Linux. Veuillez mettre à jour ce code si vous remarquez des erreurs / améliorations. Vous êtes libre d'utiliser ce code comme bon vous semble.

Pour Windows, vous utiliseriez qsort_s : http://msdn.microsoft.com/en-us/library/4xc60xas(VS.80).aspx

Apparemment, il existe une controverse sur le fait que BSD et GNU possèdent des versions incompatibles de qsort_r . qsort_r donc prudent dans son utilisation dans le code de production: http://sourceware.org/ml/libc-alpha/2008-12/msg00003.html.

BTW, le _s signifie “sécurisé” et le _r signifie “re-entrant”, mais les deux signifient qu’il y a un paramètre supplémentaire.

Ce n’est spécifié dans aucune norme de portabilité. Aussi, je pense que c’est une erreur de l’appeler une version “thread-safe” de qsort . Le qsort standard est thread-safe, mais qsort_r vous permet efficacement de passer une fermeture comme fonction de comparaison.

Évidemment, dans un environnement mono-thread, vous pouvez obtenir le même résultat avec une variable globale et qsort , mais cette utilisation ne sera pas thread-safe. Une approche différente de la sécurité des threads consiste à utiliser des données spécifiques à un thread et à demander à votre fonction de comparaison d’extraire son paramètre des données spécifiques à un thread ( pthread_getspecific avec des threads POSIX ou des variables __thread dans gcc et le prochain standard C1x).