C ++例外を使用してerrno状態を転送する場合、次のようなコードに対してg ++(4.5.3)によって生成されるコンパイル済みコード
#include <cerrno>
#include <stdexcept>
#include <string>
class oserror : public std::runtime_error {
private:
static std::string errnotostr(int errno_);
public:
explicit oserror(int errno_) :
std::runtime_error(errnotostr(errno_)) {
}
};
void test() {
throw oserror(errno);
}
かなり予想外です(Linuxではx86_64)
.type _Z4testv, @function
...
movl $16, %edi
call __cxa_allocate_exception
movq %rax, %rbx
movq %rbx, %r12
call __errno_location
movl (%rax), %eax
movl %eax, %esi
movq %r12, %rdi
call _ZN7oserrorC1Ei
これが基本的に意味するのは、C ++例外の引数としてのerrnoは、__ errno_location(errnoのマクロコンテンツ)の呼び出しに先行する__cxa_allocate_exceptionの呼び出しのため、ほとんど役に立たないということです。 errno状態を保存します(少なくとも、libstdc ++のeh_alloc.ccにある__cxa_allocate_exceptionのソースを理解している限り)。
これは、メモリ割り当てが失敗した場合、実際に例外オブジェクトに渡されるはずだったエラー番号が、std::mallocが設定したエラー番号で上書きされることを意味します。std :: mallocは、終了が成功した場合でも、とにかく既存のerrno状態を保存することを保証しません-したがって、上記のコードは一般的な場合には間違いなく壊れています。
Cygwin、x86では、test()用にコンパイルされる(g ++ 4.5.3も使用する)コードは問題ありませんが、次のようになります。
.def __Z4testv; .scl 2; .type 32; .endef
...
call ___errno
movl (%eax), %esi
movl $8, (%esp)
call ___cxa_allocate_exception
movl %eax, %ebx
movl %ebx, %eax
movl %esi, 4(%esp)
movl %eax, (%esp)
call __ZN7oserrorC1Ei
これは、ライブラリコードが例外でerrno状態を適切にラップするために、次のようなものに展開されるマクロを常に使用する必要があることを意味しますか?
int curerrno_ = errno;
throw oserror(curerrno_);
例外の場合の評価順序について何も述べていないC++標準の対応するセクションを実際に見つけることができないようですが、x86_64(Linuxの場合)でg ++で生成されたコードは、コンストラクターのパラメーターを収集する前の例外オブジェクトであり、これは何らかの形でコンパイラーのバグです。私は正しいですか、それともこれは私の側の根本的に間違った考えですか?