Visual C++のC Runtime (CRT) ライブラリは、バージョン2015 (14.0) 以降、新しい設計のUniversal CRT (UCRT) を採用するようになり、C/C++標準ライブラリが再実装されました。
その際、かなりの数のバグが混入したのですが*1*2、いまだに修正されていないバグもいくつかあります。
例えば、以下のリファレンスでは、swprintf()
でWEOF
を出力しようとした場合、戻り値はエラーコード-1
を返すことが明記されており、サンプルコードでも例示されているのですが、UCRTでは間違った値を返します。
#include <stdio.h>
int main(void)
{
wchar_t buf[100];
int len = swprintf(buf, 100, L"%s", L"Hello world");
printf("wrote %d characters\n", len);
len = swprintf(buf, 100, L"%s", L"Hello\xffff world");
printf("wrote %d characters\n", len);
}
VC2013までは、11
と-1
が正しく出力されるのですが、VC2015以降では11
と12
が出力されます。
swprintf()
系はvswprintf()
系を使って実装されており、当然のごとくvswprintf()
にも同じバグがあります。
これはWindows 10 21H1の最新版UCRT (ucrtbase.dll, 10.0.19041.789) でも修正されていません。
ちなみにWEOF
は "%ProgramFiles(x86)%\Windows Kits\10\Include\10.0.*.0\ucrt\corecrt_wstdio.h" にて以下のように定義されています。
#define WEOF ((wint_t)(0xFFFF))
UCRTは、Windows 10では標準システムコンポーネント扱いとなっており、UCRTを動的リンクしたアプリケーションは、システムディレクトリにインストールされているDLLのほうを必ずロードするようになっています。Windows 8.xおよびそれ以前のバージョンでは、アプリケーションに同梱されているプライベートDLL(ローカルDLL)があればそちらを優先的にロードするようになっています。
UCRTのソースコードはWindows SDKに付属しています。インターフェイスはCですが、内部実装はC++です。
swprintf()
の定義を追っていけば、どのファイルに実装があるか分かりますが、どうも "%ProgramFiles(x86)%\Windows Kits\10\Source\10.0.*.0\ucrt\stdio\output.cpp" 内のcommon_vsprintf()
の内部、processor_type::process()
にバグがありそうです。processor_type
はテンプレートoutput_processor
の特殊化ですが、その実装は "%ProgramFiles(x86)%\Windows Kits\10\Source\10.0.*.0\ucrt\inc\corecrt_internal_stdio_output.h" にあります。
余談ですが、C形式のAPIの内部実装にC++を使用することはよくあります。AndroidのBionic libcも、昔はすべてCとアセンブラで実装されていましたが、Android 4.xあたりで再設計されて部分的にC++で実装されるようになりました*3*4*5。なお、WindowsもAndroidも、システム開発にRust言語の活用を始めているらしいので、将来的にCランタイムライブラリもRustで実装されることになるかもしれないですね。少なくともRustコードは教養として読めるようになっておいたほうがよさそうです。