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)
}
相关推荐
可均可可6 分钟前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite
颇有几分姿色22 分钟前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
白子寰28 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_0133 分钟前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
gkdpjj38 分钟前
C++优选算法十 哈希表
c++·算法·散列表
王俊山IT40 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
-Even-43 分钟前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus
AndyFrank1 小时前
mac crontab 不能使用问题简记
linux·运维·macos
筱源源1 小时前
Kafka-linux环境部署
linux·kafka
plmm烟酒僧1 小时前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv