C++ SEH结构化异常捕获处理(双平台支持 Linux、Windows)。

测试:

cpp 复制代码
    try_ctor();
    try_call(
        []()
        {
            printf("1111111111111111111111\r\n");
            int* p = NULL;
            *p = 100;
            throw 1;

            // try_eeh();
        }, 
        []()
        {
            printf("2222222222222222222222\r\n");
        });

设置NULL指针P的值引发程式崩溃,可以被正确捕获(catch)处理,不支持 finally,可以自己改改,这是一个相对来说较为简洁的实现,该实现的代码可以在WIN、LINUX平台上编译通过且正常就绪运行。

try_call 保护代码快调用类似 lua 的 xpcall,try_ctor,注册所需的信号,try_eeh 是回到当前压入栈顶的结构化的异常处理器,vc++ 的seh 结构化处理也是这个处理,可以参考易语言seh结构化异常处理,写这个小哥是用汇编来写的,或者自己反调试看看seh展开的机器代码,思想上这个东西都差不多,但 try_catch 也有缺点,如果栈坏的很彻底就没法恢复了,比如C#.NET,单一时间内异常抛多了,catch 是捕获不了的,毕竟不是java 这类型的语言。

实现:

cpp 复制代码
class __try_context__ final {
public:
    jmp_buf                                                         __try_jmp_buf;
    bool                                                            __try_jmp_flg;

public:
    std::function<void()>                                           __try_block;
    std::function<void()>                                           __try_catch;

public:
    inline __try_context__()
        : __try_jmp_flg(false) {

    }
};

static thread_local std::list<std::shared_ptr<__try_context__>>     __try_contexts__;
static thread_local std::shared_ptr<__try_context__>                __try_eeh__;

static std::shared_ptr<__try_context__> try_seh_pop_context() noexcept {
    auto tail = __try_contexts__.begin();
    auto endl = __try_contexts__.end();
    if (tail == endl) {
        return NULL;
    }

    std::shared_ptr<__try_context__> context = std::move(*tail);
    __try_contexts__.erase(tail);
    return context;
}

static void try_seh_pop() noexcept {
    std::shared_ptr<__try_context__> context = try_seh_pop_context();
    context.reset();
}

static void try_seh_eeh_clear() noexcept {
    __try_eeh__ = NULL;
}

static void try_seh_linux(__try_context__* context) noexcept {
    int32_t signo = setjmp(context->__try_jmp_buf);
    if (signo == 0) {
        context->__try_jmp_flg = true;
        context->__try_block();
        try_seh_pop();
    }
    else {
        context = __try_eeh__.get();
        context->__try_catch();
    }

    try_seh_eeh_clear();
}

#ifdef _MSC_VER
static void try_seh_windows(__try_context__* context) {
    __try {
        try_seh_linux(context);
    }
    __except (sehCrashFilter(GetExceptionCode(), GetExceptionInformation())) { /* GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH */
        context->__try_catch();

        try_seh_pop();
        try_seh_eeh_clear();
    }
}
#endif

void try_call(std::function<void()>&& __try_block, std::function<void()>&& __try_catch) {
    if (NULL == __try_block) {
        throw std::runtime_error("__try_block argument not allow is NULL.");
    }

    __try_context__* context = NULL;
    if (NULL == __try_catch) {
        throw std::runtime_error("__try_catch argument not allow is NULL.");
    }
    else
    {
        std::shared_ptr<__try_context__> try_context = std::make_shared<__try_context__>();
        if (NULL == try_context) {
            throw std::runtime_error("unable to make try context.");
        }
        else {
            context = try_context.get();
        }

        try_context->__try_block = std::move(__try_block);
        try_context->__try_catch = std::move(__try_catch);
        __try_contexts__.emplace_back(try_context);
    }

#ifdef _MSC_VER
    try_seh_windows(context);
#else
    try_seh_linux(context);
#endif
}

bool try_eeh() {
    jmp_buf __try_jmp_buf;
    for (;;) {
        {
            std::shared_ptr<__try_context__> context = try_seh_pop_context();
            if (NULL == context) {
                break;
            }

            if (!context->__try_jmp_flg) {
                continue;
            }
            else {
                __try_eeh__ = context;
            }

            memcpy(__try_jmp_buf, context->__try_jmp_buf, sizeof(__try_jmp_buf));
        }

        longjmp(__try_jmp_buf, 1);
        return true;
    }
    return false;
}

void try_ctor() {
    auto __try_eeh__ = [](int signo) noexcept -> void {
        bool ok = try_eeh();
        if (!ok) {
            signal(signo, SIG_DFL);
            raise(signo);
        }
    };

#ifdef __GNUC__
    signal(SIGTRAP, __try_eeh__);   // 调试陷阱
    signal(SIGBUS, __try_eeh__);    // 总线错误(常见于结构对齐问题)
    signal(SIGQUIT, __try_eeh__);   // CTRL+\退出终端
    signal(SIGSTKFLT, __try_eeh__); // 进程堆栈崩坏
#endif

    signal(SIGSEGV, __try_eeh__);   // 段错误(试图访问无效地址)
    signal(SIGFPE, __try_eeh__);    // 致命的算术运算问题(常见于试图除以零或者FPU/IEEE-754浮点数问题)
    signal(SIGABRT, __try_eeh__);   // 程式被中止执行(常见于三方库或固有程式遭遇一般性错误执行abort()强行关闭主板程式)
    signal(SIGILL, __try_eeh__);    // 非法硬件指令(CPU/RING 0 ABORT)
}
相关推荐
Felven1 小时前
stress-ng 与 fio 性能测试工具使用指南
linux·测试工具·fio·stress-ng
by209991 小时前
vector 的模拟实现:扩容与边界的详细阐述(下)
c++·经验分享·vector·动态数组
huainingning2 小时前
RJ SW Console口忘记密码处理方法
linux·运维·服务器
布裘2 小时前
【银河麒麟】桌面系统循环登录,无法进入桌面?
linux·运维·服务器
慧都小项2 小时前
程序到了Linux才出错?用CLion把调试接到目标环境
linux·运维·服务器
kakakahahahaha3 小时前
Win10开始菜单打不开排查:explorer、AppX注册、SFC、服务与用户配置修复
windows·电脑·笔记本电脑·内容运营·软件需求
ITHAOGE154 小时前
下载 | Windows Server 2016最新纯净版ISO映像!(集成9月更新、标准版、数据中心版、14393.9514)
windows·科技·微软·电脑
余额瞒着我当琳4 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
青少儿编程课堂4 小时前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
Ruiery5 小时前
Linux 6.6内核内存管理深度解析(一):物理内存初始化 — memblock 怎么把内存交给 buddy
linux·运维·服务器