*当*做C++异常时会爆炸C代码

use*_*329 3 c c++ gcc exception

好的,所以我需要-fexceptions允许C++异常通过C代码传播.我试图比较C++和C的结果,从我所知道的,编译的例程在程序集中是相同的,即使没有那个选项[1].这是测试:

#include <unistd.h>

typedef int (*Callback)(void* param,void* buffer,int n);

void write_wrapper(int fd,const void* buffer,int n)
{
    const char* temp=(const char*)buffer;
    while(n!=0)
        {
        int k=write(fd,temp,n);
        n-=k;
        temp+=k;
        }
}

int test(Callback cb,void* cb_param)
{
    char buffer[1024];
    int n=0;
    do
        {
        n=cb(cb_param,buffer,1024);
        write_wrapper(STDOUT_FILENO,buffer,n);
        }
    while(n!=1024);
}

int test2(Callback cb,void* cb_param)
{
char more_stack_space_please[1024]={0};
cb(cb_param,more_stack_space_please,1024);
write_wrapper(STDOUT_FILENO,more_stack_space_please,1024);
test(cb,cb_param);
}
Run Code Online (Sandbox Code Playgroud)

在这里,由调用者来释放资源,所以即使回调函数抛出异常也不应该泄漏.

即使这个例子似乎工作(使用简单的C++驱动程序测试)

#include "lib.h"
#include <cstdio>

class Resource
    {
    public:
        Resource()
            {fprintf(stderr,"A Resource\n");}

        ~Resource()
            {fprintf(stderr,"Not a Resource\n");}
    };

int main()
    {
    try
        {
        Resource foo;
        test([](void* cb_param,void* buffer, int n)->int
            {
            Resource bar;
            throw "test";
            },nullptr);
        test2([](void* cb_param,void* buffer, int n)->int
            {
            throw "test2";
            },nullptr);
        }
    catch(const char* err)
        {
        fprintf(stderr,"Error: %s\n",err);
        return -1;
        }
    return 0;
    }
Run Code Online (Sandbox Code Playgroud)

,我从GTK回调中得到了未捕获的异常错误.我的想法:

  • GTK是一个共享库,所以它可能只影响链接器,而不影响代码生成器
  • GTK(和GLib)包含许多奇怪的黑客,这可能会搞砸了

[1] https://godbolt.org/g/HfSnjB

mks*_*eve 8

这个问题的问题是,它询问未定义结果的定义行为是什么.

C标准没有描述通过C函数抛出异常时的行为,因为它不应该发生.

C++标准没有解释通过C函数丢弃未捕获异常的机制,因为它不应该发生.

一个实际的例子

Windows Visual C++使用fs:segment寄存器进行线程本地存储,并使用线程局部数据段中的特定插槽来创建catch帧的链接列表.

抛出C++异常时,将检查链表以查找堆栈对象的析构函数和合适的catch框架.

C编译器可能不知道这些资源的C++使用,并且能够为不同目的重新使用这些插槽.如果插槽用于不兼容的功能,则会发生崩溃.

一个特定的平台和编译器可能会支持这一点,但你可以随心所欲地使用它.

gcc是一个C编译器,因此不会抛出异常.它可以创建支持异常的代码-fexception.

gnu编译器:使用异常建议用-fexceptions编译C代码并说

特别是,展开到没有异常处理数据的帧将导致运行时中止.

虽然该语句的上下文没有描述是由于被调试子句,还是由于异常机制识别非法状态并导致中止.

实际的实现将是硬件和操作系统特定的,这在问题中没有指定,对于特定的答案,应该指定这些.

  • C++没有兼容性ABI.调用使用不同编译器编译的C++对象并不能保证工作.
  • 不建议在不同模块之间抛出异常,因为即使编译器上的实现也产生了不符合内部版本的代码.
  • 假设您可以控制C和C++代码,通过启用异常并可能将C编译为C++来确保C是安全的(使用extern "C"包装器,确保它是完全C++编译单元).