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)
}
相关推荐
励志不掉头发的内向程序员10 小时前
【LibreCAD 2D架构】从 addHistory 到 RS_UndoCycle:LibreCAD 里的两套 History 与撤销机制
开发语言·c++·qt·学习·系统架构
Escalating_xu10 小时前
【mmap 进程间通信】从共享内存到跨进程唤醒:用互斥锁、条件变量控制多个进程
java·linux·开发语言·jvm
djjjx.10 小时前
Linux工具篇 (三):make与Makefile
linux·服务器·makefile·make
elseif12310 小时前
【双指针/二分】P1102 A-B 数对
c++·算法·二分·双指针
雪落漂泊10 小时前
C++智能指针
开发语言·jvm·c++
xiaoye-duck10 小时前
《Linux 网络编程》深入理解 TCP 协议(五):滑动窗口、丢包重传与流量控制详解
linux·网络·tcp
昌原的儿子LEO11 小时前
【单片机入门】UART 串行异步通信核心考点梳理
linux·单片机·51单片机
挂科边缘11 小时前
Android Studio 保姆级安装教程,Windows 电脑上安装 Android Studio
android·windows·android studio
团子股股东峥哥11 小时前
day36-RHEL-管理基本存储
linux·运维·服务器
Fcy64811 小时前
Linux下 网络计算器小程序(socket封装、协议自定义、业务逻辑解耦)
linux·网络·自定义协议