Khi sử dụng ngoại lệ C++ để chuyển trạng thái errno, mã được biên dịch được tạo ra bởi g ++ (4.5.3) cho mã như sauLuồng kiểm soát bất ngờ (trình biên dịch lỗi?) Sử dụng errno làm đối số ngoại lệ trong C++ (g ++)
#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);
}
là khá bất ngờ (trên 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
Điều này về cơ bản có nghĩa là errno như một tham số để một C++ ngoại lệ là khá nhiều vô ích do các cuộc gọi đến __cxa_allocate_exception trước cuộc gọi đến __errno_location (đó là nội dung macro của errn o), nơi mà trước đây gọi std :: malloc và không lưu trạng thái errno (ít nhất là theo như tôi hiểu các nguồn của __cxa_allocate_exception trong eh_alloc.cc của libstdC++).
Điều này có nghĩa là trong trường hợp phân bổ bộ nhớ không thành công, số lỗi thực sự được chuyển vào đối tượng ngoại lệ sẽ bị ghi đè bằng số lỗi std :: malloc setup. std :: malloc không đảm bảo lưu trạng thái errno hiện tại, dù sao, ngay cả trong trường hợp thoát thành công - vì vậy mã trên chắc chắn bị hỏng trong trường hợp chung.
On Cygwin, x86, mã đó được biên dịch (cũng sử dụng g ++ 4.5.3) cho thử nghiệm() là okay, mặc dù:
.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
Điều này có nghĩa rằng cho mã thư viện để quấn đúng errno nhà nước trong một ngoại lệ, tôi sẽ luôn luôn phải sử dụng một macro mà mở rộng đến một cái gì đó giống như
int curerrno_ = errno;
throw oserror(curerrno_);
tôi thực sự dường như không thể tìm thấy những phần tương ứng của tiêu chuẩn C++ mà nói bất cứ điều gì về trật tự đánh giá trong trường hợp ngoại lệ, nhưng với tôi có vẻ như mã g ++ được tạo trên x86_64 (o n Linux) bị hỏng do cấp phát bộ nhớ cho đối tượng ngoại lệ trước khi thu thập các tham số cho hàm tạo của nó và đây là lỗi trình biên dịch theo một cách nào đó. Tôi có đúng không, hay đây là một số suy nghĩ cơ bản sai về phía tôi?
'errnotostr()' là một hàm (tĩnh) của 'lớp oserror' về cơ bản gọi' strerror_r() 'và trả về một dạng' std :: string'-ified của kết quả. Tôi đã không bao gồm một phần của mã, bởi vì nó không liên quan cho ví dụ. – modelnine
Có, brainfart, xin lỗi. Xem thường. – irobot