2010-11-29 20 views
6

qsort_r()qsort()のリエントラントバージョンで、追加の 'thunk'引数を受け取り、これをcompare関数に渡して、移植可能なCコードで使用できるようにしたいと考えています。 qsort()はPOSIXであり、どこでもqsort_r()はBSD拡張のようです。具体的な質問として、Windows Cランタイムにこれが存在するのか、それとも同等の機能がありますか? Windowsの場合qsortと比較して、re-entrant qsort_r関数はどのように移植可能ですか?

答えて

7

例で示したqsort_r/qsort_s(sort_rと呼ばれる)のポータブルバージョンを作成しようとしました。私はまた、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 
} 

使用例:

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

コンパイル/実行/出力:私はMac上でテストしてみた

$ 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 

Linux &間違いや改善を見つけた場合は、このコードを更新してください。このコードは自由に使用できます。

7

あなたがqsort_sを使用します。http://msdn.microsoft.com/en-us/library/4xc60xas(VS.80).aspx

どうやらqsort_rの互換性のないバージョンを持つBSDやGNUについてのいくつかの論争があるので、生産コードでそれを使用するには注意が:http://sourceware.org/ml/libc-alpha/2008-12/msg00003.html

ところで、_sを「安全」を表し、_rは「再入可能」を表しますが、両方とも追加のパラメータがあることを意味します。

5

ポータビリティ標準では指定されていません。また、それはqsortの "スレッドセーフ"バージョンと呼ぶのは間違いだと思います。標準qsortはスレッドセーフですが、qsort_rは比較関数としてクロージャを渡すことができます。

明らかに、シングルスレッド環境では、グローバル変数とqsortで同じ結果を得ることができますが、この使用法はスレッドセーフではありません。スレッドセーフに関する別のアプローチは、スレッド固有のデータを使用して、スレッド固有のデータ(pthread_getspecific POSIXスレッド、またはgccの変数__thread、今後のC1x標準)からパラメータを取得することです。

+1

あなたはそうです。これはスレッドセーフではなく、リエントラントです。つまり、シングルスレッド環境でもそれが必要な場合があります。 – Gabe

+0

'qsort'関数自体はリエントラントであるか、少なくともそれは正常な実装でなければなりません。問題は、引数リエントラントを必要とする比較関数を使って 'qsort'を呼び出す関数を作ることです。 –

+0

もちろん、 'qsort'アルゴリズムはグローバルな状態を必要としないので、事実上リエントラントで、スレッドセーフです。私はちょうど 'qsort_r'が再入門が必要な場合に使用することを意図しているので、スレッドローカルストレージを使用しても必ずしも同じ結果が得られるわけではありません。 – Gabe

関連する問題