How portable is the re-entrant qsort_r function compared to qsort?

后端 未结 3 1620
面向向阳花
面向向阳花 2020-12-06 17:36

qsort_r() is the re-entrant version of qsort() which takes an additional \'thunk\' argument and passes it into the compare function and I\'d like t

相关标签:
3条回答
  • 2020-12-06 18:08

    I've attempted to write a portable version of qsort_r / qsort_s (called sort_r) shown with an example. I've also put this code in a git repo (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
    }
    

    Example usage:

    #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");
    }
    

    Compile/run/output:

    $ 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
    

    I've tested on mac & linux. Please update this code if you spot mistakes / improvement. You are free to use this code as you wish.

    0 讨论(0)
  • 2020-12-06 18:10

    For Windows you would use qsort_s: http://msdn.microsoft.com/en-us/library/4xc60xas(VS.80).aspx

    Apparently there is some controversy about BSD and GNU having incompatible versions of qsort_r, so be careful about using it in production code: http://sourceware.org/ml/libc-alpha/2008-12/msg00003.html

    BTW, the _s stands for "secure" and the _r stands for "re-entrant", but both mean that there's an extra parameter.

    0 讨论(0)
  • 2020-12-06 18:22

    It's not specified in any portability standard. Also I think it's a mistake to call it a "thread-safe" version of qsort. The standard qsort is thread-safe, but qsort_r effectively allows you to pass a closure as your comparison function.

    Obviously in a single-threaded environment, you can achieve the same result with a global variable and qsort, but this usage will not be thread-safe. A different approach to thread-safety would be to use thread-specific data and have your comparison function retrieve its parameter from the thread-specific data (pthread_getspecific with POSIX threads, or __thread variables in gcc and the upcoming C1x standard).

    0 讨论(0)
提交回复
热议问题