MinGWでコンパイルしたC++バイナリのサイズ増加を抑える

libstdc++.dllを配布するのは面倒なので、libstdc++を静的リンクすることはよく行われると思う。
しかし、静的リンクするとそれだけでファイルサイズが600KBほど増加してしまう[^1],[^2]。

これはnmやマップファイルから当たりをつけることができて、このようなファイルをソースに加えると避けることができる[^3]。
`x86_64-w64-mingw32-g++ -O2 src.cpp winexcept.cpp -static-libgcc -static-libstdc++ -s`で確認済みです。

```cpp:winexcept.cpp
// This file contains hack to avoid huge binary when static linking libstdc++.
// for libc++, defining _LIBCPP_NO_EXCEPTIONS is enough.
// Note: these symbols must NOT be defined as weak.
// If symbol duplication occurs, what you should do is to find new exception from libstdc++ and add those symbols here.
#include <stdio.h>
#include <stdlib.h>
extern "C"{ static inline void puts_and_exit(const char* p){puts(p);exit(1);} }
namespace std{
	// for symbols those should be defined, refer:
	// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/functexcept.c
	void __throw_bad_exception(){puts("bad_exception");exit(1);}
	void __throw_bad_alloc(){puts("bad_alloc");exit(1);}
	void __throw_bad_array_new_length(){puts("bad_array_new_length");exit(1);}
	void __throw_bad_cast(){puts("bad_cast");exit(1);}
	void __throw_bad_typeid(){puts("bad_typeid");exit(1);}

	void __throw_logic_error(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_domain_error(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_invalid_argument(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_length_error(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_out_of_range(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_out_of_range_fmt(const char* p, ...){puts(p);exit(1);}
	void __throw_runtime_error(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_range_error(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_overflow_error(const char* p) __attribute((alias("puts_and_exit")));
	void __throw_underflow_error(const char* p) __attribute((alias("puts_and_exit")));

    // https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/cxx11-ios_failure.cc
	void __throw_ios_failure(const char* p){printf("ios_failure %s\n",p);exit(1);}
	void __throw_ios_failure(const char* p, int n){printf("ios_failure %s %d\n",p,n);exit(1);}
	// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/system_error.cc
	void __throw_system_error(int n){printf("system_error %d\n",n);exit(1);}
	// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/future.cc
	void __throw_future_error(int n){printf("future_error %d\n",n);exit(1);}
	// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/functional.cc
	void __throw_bad_function_call(){puts("bad_function_call");exit(1);}
}
```

\# あ、iostream系使った場合は無力ですので、その辺はCで頑張るようにするしかありません。あくまでもvectorとかmapとかのあたり。

[^1]: これはThread model: win32の場合であって、Thread model: posixの場合は実は大丈夫らしい。ただこの場合は`-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive,-Bdynamic`なるおまじないが別途必要なのだが。ちなみにDebianではwin32版、macOS Homebrewではposix版がインストールされます。
[^2]: https://web.archive.org/web/20200704231225/http://www.mingw.org/wiki/Large_executables
[^3]: 例外を握りつぶすということなので、軽量なソフト[^4]以外では非推奨
[^4]: 個人開発でもいわゆる製品に見えるような数十MB超えのソフトでは例外はやはり握りつぶさないほうがいいと思います