こんにちは高木です。今回はC言語の紛らわしいライブラリ関数についての話題です。
C言語のライブラリ関数は、処理系によって似て非なるものが存在します。Visual C++のqsort_sとGNU C Libraryのqsort_rとC11のqsort_sがなさにそれです。これらを使い分けるには、よほど注意しておかないと混乱してしまいそうです。
まず、Visual C++のqsort_sですが、次のような形式になっています。
0 1 2 3 4 5 6 7 8 |
void qsort_s( void *base, size_t num, size_t width, int (__cdecl *compare )(void *, const void *, const void *), void * context ); |
次に、GNU C Libraryのqsort_rは次のような形式になっています。
0 1 2 3 4 |
void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *)); |
VC++のqsort_sとGNU C Libraryのqsort_rは、エラー処理は違いますが、比較関数に比較対象以外の引数を与えられるという点では同じです。contextまたはthunkがそれにあたります。qsort_sのcontextとqsort_rのthunkでは引数の順番が異なりますが、比較関数が受け取る引数の順番は同じです。
つぎに、C11のqsort_sを見てみましょう。Annex KのK.3.6.3.2 The qsort_s functionに掲載された形式は次のようになっています。
0 1 2 3 4 5 |
errno_t qsort_s(void *base, rsize_t nmemb, rsize_t size, int (*compar)(const void *x, const void *y, void *context), void *context); |
contextが最後なので、一見するとVisual C++のものと同じだと錯覚しそうになるのですが、よく見ると似ても似つかないものであることがわかります。そもそも、Visual C++のqsort_sは返却値を返しませんがC11のqsort_sは返します。それだけを考えてもまったく別のものです。
比較関数に渡すcontextは、VC++では第1引数だったのに対し、C11では第3引数になっています。C言語なので、間違って定義しても、警告が出ることはあってもエラーにならないことも要注意です。
こんなに違う関数が同名であってもよいのでしょうか? Visual C++はC11に対応しているようでいて、ライブラリ関数についてはそうでもありません。それならそれで、_qsort_sのような名前にすべきではないのでしょうか? かつてMicrosoft C/C++ Compiler 7.0で_strlwrのように下線を付けて回ったのは何だったのでしょうか? ついでにいえば、そのときなぜisleadbyteには下線を付けなかったのでしょうか?
VC++が後方互換性を保ちながらC11にも対応するには、__STDC_LIB_EXT1__および__STDC_WANT_LIB_EXT1__マクロをうまく活用する方法が考えられます。このマクロを使えば、外付けでC11に対応することもある程度できるかもしれませんね。
ちなみに、bsearch_sに関しても、Visual C++とC11では比較関数の引数の順序が異なります。また、GNU C Libraryのbsearch_rはちょっと見た感じではC11のbsearch_sにそっくりのようです。