qsort_r()
es la versión re-entrante de qsort()
que toma un argumento 'thunk' adicional y lo pasa a la función de comparación y me gustaría poder usarlo en el código C portátil. qsort()
es POSIX y en todas partes, pero qsort_r()
parece ser una extensión BSD. Como pregunta específica, ¿existe esto o tiene un equivalente en el tiempo de ejecución de Windows C?¿Cuán portátil es la función qsort_r reentrante en comparación con qsort?
Respuesta
He intentado escribir una versión portátil de qsort_r/qsort_s (llamada sort_r) que se muestra con un ejemplo. También he poner este código en un repositorio 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
}
Ejemplo de uso:
#include <stdio.h>
/* 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");
}
Compilar/ejecución/salida:
$ 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
He probado en Mac & linux. Actualice este código si detecta errores/mejoras. Usted es libre de usar este código como lo desee.
Para Windows que usaría qsort_s
: http://msdn.microsoft.com/en-us/library/4xc60xas(VS.80).aspx
Al parecer hay cierta controversia sobre BSD y GNU tener versiones incompatibles de qsort_r
, así que tenga cuidado acerca de su uso en el código de producción: http://sourceware.org/ml/libc-alpha/2008-12/msg00003.html
Por cierto, el _s
significa "seguro" y el _r
significa "reentrada", pero ambos significan que hay un parámetro adicional.
No está especificado en ningún estándar de portabilidad. También creo que es un error llamarlo una versión "thread-safe" de qsort
. El estándar qsort
es seguro para subprocesos, pero qsort_r
le permite aprobar un cierre como su función de comparación.
Obviamente, en un entorno de subproceso único, puede lograr el mismo resultado con una variable global y qsort
, pero este uso no será seguro para subprocesos. Un enfoque diferente para la seguridad de subprocesos sería usar datos específicos de subprocesos y hacer que su función de comparación recupere su parámetro de los datos específicos de subprocesos (pthread_getspecific
con subprocesos POSIX o variables __thread
en gcc y el próximo estándar C1x).
- 1. ¿Cuán necesario o conveniente es escribir SQL portátil?
- 2. Estabilizando la biblioteca estándar qsort?
- 3. problema al intentar utilizar la función C qsort
- 4. ¿El qsort de stdlib es recursivo?
- 5. ¿Cuán maduro es dblinq?
- 6. cuán seguro es tomcat
- 7. ¿Cuán universalmente es compatible con C99?
- 8. malloc() no es reentrante pero es seguro para subprocesos?
- 9. ¿Cuán escalable es Grails?
- 10. Cuán maduro es PHP: SVN
- 11. ¿Cuán importante es la portabilidad de SQL?
- 12. ¿Cuán importante es la compatibilidad con versiones anteriores?
- 13. ¿Wiki es portátil?
- 14. ¿Cómo pasar un método a qsort?
- 15. ¿Cuán precisa es la precisión de CLLocation?
- 16. ¿cuán corto debe ser una función?
- 17. ¿Cuán exacto es Thread.Sleep (TimeSpan)?
- 18. cómo portátil es mmap
- 19. ¿Cuán flexible es Oracle ADF?
- 20. La versión LIB es enorme en comparación con la depuración
- 21. Ordenando la inserción con una función de comparación
- 22. ¿Cuán compatible es el software SIP VoIP en la red?
- 23. cuán precisa es la medición de altitud en teléfonos móviles
- 24. ¿Cuán seguro es mi regreso seguro?
- 25. ¿Cuán escalable es Codeigniter V Otros marcos PHP?
- 26. ¿Cuál es la mejor comparación con 0 en PHP?
- 27. ¿Qué tan portátil es C++?
- 28. ¿Qué tan portátil es GLib?
- 29. Rendimiento de qsort vs std :: sort?
- 30. OSGi: ¿Cuán madura es esta tecnología?
Tienes razón. No es seguro para subprocesos, es reentrante. Eso significa que aún puede necesitarlo incluso en entornos de un solo subproceso. – Gabe
La función 'qsort' en sí misma es reentrante, o al menos debe estar en cualquier implementación correcta. El problema es crear funciones que quieran llamar a 'qsort' con una función de comparación que requiera la reentrada de un argumento. –
Sí, obviamente el algoritmo 'qsort' no requiere estado global, por lo que es de-facto re-entrant y thread-safe. Solo quise decir que 'qsort_r' está destinado a ser usado en lugares donde pueda ser necesaria la reentrada y, por lo tanto, el uso de almacenamiento local de subprocesos no siempre logra el mismo resultado. – Gabe