当使用 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
这基本上意味着 errno 作为 C++ 异常的参数几乎没有用,因为在调用 __errno_location(这是 errno 的宏内容)之前调用 __cxa_allocate_exception,前者调用 std::malloc并且不保存 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++ 生成代码似乎由于以下原因而损坏在为其构造函数收集参数之前为异常对象分配内存,这在某种程度上是编译器错误。我是对的,还是我的一些根本错误的想法?
最佳答案
What this basically means is that errno as an argument to a C++ exception is pretty much useless due to the call to __cxa_allocate_exception preceding the call to __errno_location (which is the macro content of errno), where the former calls std::malloc and does not save errno state (at least as far as I understood the sources of __cxa_allocate_exception in eh_alloc.cc of libstdc++).
这不是真的。就我检查过的源代码而言,__cxa_allocate_exception
中唯一可以更改 errno
的“东西”是 malloc()
。可能会出现两种情况:
malloc()
成功,则errno
不变;malloc()
失败,然后std::terminate()
被调用并且您的oserror()
永远不会被构建。
因此,由于在调用构造函数之前调用 _cxa_allocate_exception
不会在功能上改变您的程序,我相信 g++ 有权这样做。
关于c++ - 在 C++ (g++) 中使用 errno 作为异常参数的意外控制流(编译器错误?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9984034/