Visual C++ での C99 complex

最近のVisual C++ではC99もある程度サポートしているらしい。

上記ブログによると、 complex.h の関数もサポートしているらしい。しかし、コンパイラーは _Complex 型をサポートしていない。_Complex 型をサポートしていないのに、 complex.h の関数をサポートしているとはどういうことか。一体、 complex.h の関数は何を受け取って何を返すのか。

確認のため、次のような簡単な C++ のプログラムを書いてみた。

#include <stdio.h>
#include <complex.h>
#include <typeinfo>
int main(int argc, char *argv[]) {
    puts(typeid(cabs).name());
}

結果は…。

double __cdecl(struct _C_double_complex)

どうやら、 _C_double_complex という構造体が定義されて、それが double _Complex 型の代わりをしているようだ。多分この構造体の中身は、要素数2の double の配列とかそんなんだろう。下記のコードは動いた。

#include <complex.h>
#include <stdio.h>
int main(int argc, char * argv[]) {
    _C_double_complex z = {1.0, 1.0};
    // z = _Complex_I; /* error C2440: '=': cannot convert from '_Fcomplex' to '_C_double_complex' */
    printf("%g\n", cabs(z));
}

_Complex_I も定義されているようだが、型が違って、自動では変換されないっぽい。_C_double_complex はコンパイラー的には特別扱いも何もない普通の構造体のようで、四則演算とか double 型との変換は何もできない。

ただ、_C_double_complex 型とC99の double _Complex 型にはバイナリレベルの互換性はあるはずなので、 msvcrt を使うC99対応コンパイラーで msvcrt に含まれる C99 complex の関数を利用できる可能性はある。

MinGW からの利用

msvcrt を使うC99対応コンパイラーと言えば、MinGWだ。まずは、MinGWのリンク先CRTを msvcrt.dll (VC6)ではなく msvcr120.dll (VC2013)に変える。(本当は最新の msvcr140.dll にリンクしたいところだが、手元のMinGWにはインポートライブラリが用意されていなかった)

この辺を変えるには、 specs ファイルというのをいじればいいらしい。$ gcc -dumpspecs でデフォルトの specs ファイルを吐かせて、 -lmsvcrt という箇所を -lmsvcr120 に変える。

ただ、これだけでは cabs 等のC99関数はMinGW側が用意したものが使われてしまうようだ。MinGW側が用意した cabs 関数は libmingwex.a というファイルに含まれるようなので、これと msvcr120 をリンクする順番を入れ替える。

手順(MSYS2 の bash 上で実行):

$ gcc -dumpspecs | sed -e "s|-lmingwex %\{!mcrtdll=*:-lmsvcrt}|%\{!mcrtdll=*:-lmsvcr120} -lmingwex|" > msvcr120.specs

実際のプログラムで試してみる:

$ cat test.c
#include <complex.h>
#include <stdio.h>
int main(int argc, char * argv[]) {
    double complex z = 1.0 + 1.0 * _Complex_I;
    printf("%g\n", cabs(z));
}
$ gcc -std=gnu99 -specs=msvcr120.specs test.c
$ ./a.exe
1.41421
$ objdump -p a.exe | grep -i -A 40 msvcr120.dll
	DLL Name: MSVCR120.dll
...
	86b0	 1082  _onexit
	86ba	 1147  _set_invalid_parameter_handler
	86dc	 1320  _unlock
	86e6	 1552  abort
	86ee	 1581  cabs ←msvcr120.dll の cabs 関数が使われている!
	86f6	 1590  calloc
	8700	 1674  exit
	8708	 1722  fprintf
...

参考にしたページ:MinGW GCCでlibcとしてmsvcrt.dll以外を使うまとめ – 自分についてのまとめサイト

結局

Visual C++ には _Complex 型のサポートはないし、MinGW には独自のC99サポートがあるし、やっぱり Visual C++ の complex.h の使いどころがわからん。別に、あったところで困るものではないが、なあ。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です