c++ - 在 C++ (g++) 中使用 errno 作为异常参数的意外控制流(编译器错误?)

标签 c++ exception g++ errno

当使用 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/

相关文章:

c++ - concat 中的可变参数模板和推断的返回类型

c++ - G++ -D 选项添加数字 1

c++ - 更改函数以返回指针

c# - "The format of the URI could not be determined"与 WebRequest

c++ - 如何在队列开头添加值

java.lang.NumberFormatException : For input string: "22"

javascript - vert-x 3.3.2 http客户端异常处理程序

c++ - 为mingw编译linux库

c++ - C++ 中非常特殊的链接器错误

c++ - 如何在 C++ 中创建正确的对象层次结构