最近需要在 go 中去调用 .so 库去完成一些事情,go 方面,利用 cgo 可以顺利的调用 .so 中的方法,但是有个问题是 go 没法捕获 .so 那边出现的异常。如果 .so 那边异常了,那么会带崩 go 程序,这不是我们想看到的。例如在 服务器应用中,某个异常的请求可能会把服务器进程给弄挂,这不是我们想看到的。

我们最好在可能会崩溃的地方进行异常捕获,可以做一层 wrapper,然后将错误信息传给 go 这边,让 go 去决定异常的处理方式,这里我写了一个简单的 Demo 进行验证。

首先我们写一个简单的 cpp 的库,做成 .so

  • foo 函数增加了 try catch,将异常信息通过 char* 返回给 go;
  • foo1 函数值抛出异常,不捕获;
// clib.h
#ifdef __cplusplus
extern "C" {
#endif #include <stdlib.h> typedef struct HANDLE_ERR {
char *data;
const char *pstrErr;
} HANDLE_ERR; HANDLE_ERR foo (const char *pstrArgs); char* foo1 (const char *pstrArgs); #ifdef __cplusplus
}
#endif // clib.cpp
#include <string.h>
#include <stdio.h>
#include <exception>
#include "clib.h" struct MyException : public std::exception
{
const char * what () const throw ()
{
return "C++ Exception";
}
}; HANDLE_ERR foo (const char *pstrArgs) {
HANDLE_ERR result = {0};
try {
if (pstrArgs == nullptr)
throw MyException();
result.data = "Hello word!";
} catch(MyException &e) {
result.data = nullptr;
result.pstrErr = strdup(e.what());
}
return result;
} char* foo1 (const char *pstrArgs) {
if (pstrArgs == nullptr)
throw "exception, nullptr";
char* data = "Hello word!";
return data;
}

我们用 g++ 来做成 .so,将上面的 cpp 做成 libclib.so 文件,供 go 来使用,下面是用 g++ 编译的指令命令。

g++ -c -fPIC clib.cpp
g++ -shared -fPIC -o libclib.so clib.o

在做好了 .so 后,我们用 go 来调用

  • passNullptr 传递一个空指针给 clib,clib 会抛出异常;
  • passNormal 传递正常的值
  • passNullptrNoException 传递空指针给 foo1 函数,foo1 没有捕获异常。
package main

// #cgo LDFLAGS: -L. -lclib
// #include <stdlib.h>
// #include "clib.h"
import "C" import (
"log"
"unsafe"
) func passNullptr() {
log.Println("1. passNullptr")
ret := C.foo(nil)
if ret.pstrErr != nil {
defer C.free(unsafe.Pointer(ret.pstrErr))
log.Println("clib error: ", C.GoString(ret.pstrErr))
} else {
log.Println("no error! from clib: ", C.GoString(ret.data))
}
log.Println("")
} func passNormal() {
log.Println("2. passNormal")
cArgs := C.CString("")
defer C.free(unsafe.Pointer(cArgs))
ret := C.foo(cArgs)
if ret.pstrErr != nil {
defer C.free(unsafe.Pointer(ret.pstrErr))
log.Println("clib error: ", C.GoString(ret.pstrErr))
} else {
log.Println("no error! from clib: ", C.GoString(ret.data))
}
log.Println("")
} func passNullptrNoException() {
log.Println("3. passNullptrNoException")
cArgs := C.CString("")
defer C.free(unsafe.Pointer(cArgs))
C.foo1(nil)
log.Println("")
} func main() {
passNullptr()
passNormal()
passNullptrNoException()
}

下面是输出的结果

2022/08/27 15:47:21 1. passNullptr
2022/08/27 15:47:21 clib error: C++ Exception
2022/08/27 15:47:21
2022/08/27 15:47:21 2. passNormal
2022/08/27 15:47:21 no error! from clib: Hello word!
2022/08/27 15:47:21
2022/08/27 15:47:21 3. passNullptrNoException
libc++abi: terminating with uncaught exception of type char const*
SIGABRT: abort
PC=0x7ff811758112 m=0 sigcode=0 goroutine 0 [idle]:
runtime: unknown pc 0x7ff811758112
stack: frame={sp:0x7ff7bfefed28, fp:0x0} stack=[0x7ff7bfe803e8,0x7ff7bfeff450)
...
...
exit status 2

我们可以看到,加了 try catch 后,clib 将 error 信息传递到了 go 侧,如果不 try catch 异常,clib 的异常会把 go 进行给带崩溃,所以在 go 调用 .so 的时候,最好做一层 wrapper 做一下异常处理。

[Golang] cgo 调用 .so 捕获异常问题的更多相关文章

  1. golang cgo 使用总结

    原文地址 CGO 提供了 golang 和 C 语言相互调用的机制.某些第三方库可能只有 C/C++ 的实现,完全用纯 golang 的实现可能工程浩大,这时候 CGO 就派上用场了.可以通 CGO ...

  2. Dapr Golang HTTP 调用

    Dapr Golang HTTP 调用 版本介绍 Go 版本:1.15 Dapr Go SKD 版本:0.11.1 工程结构 从上图可知,新建 3 个 Go 启动项目,cmd 为启动项目目录,其中 c ...

  3. Linux golang使用cgo调用C++标准库问题

    我们知道cgo无法直接调用c++方法,但是可以通过c包装c++方法,以达到使用的目的. C++中,我们经常会用到STL.在cgo中,如果要调用STL,需要作如下操作: //cgo LDFLAGS: - ...

  4. golang通过cgo调用lua

    目录 1.前期准备 2.测试go代码 3.完成的一个学习项目 4.总结 1.前期准备 1.第三方库:https://github.com/aarzilli/golua 2.下载lua源码:https: ...

  5. golang动态调用方法

    package main import ( "fmt" "reflect" ) type YourT1 struct { } func (y *YourT1) ...

  6. C/C++调用Golang 二

    C/C++调用Golang 二 <C/C++调用Golang 一>简单介绍了C/C++调用Golang的方法步骤,只涉及一个简单的函数调用.本文总结具体项目中的使用场景,将介绍三种较复杂的 ...

  7. Golang调用Python

    https://yq.aliyun.com/articles/117329 Python是时髦的机器学习御用开发语言,Golang是大红大紫的新时代后端开发语言.Python很适合让搞算法的写写模型, ...

  8. electron/nodejs实现调用golang函数

    https://www.jianshu.com/p/a3be0d206d4c 思路 golang 支持编译成c shared library, 也就是系统中常见的.so(windows下是dll)后缀 ...

  9. Golang 调用 Python 代码

    go 中的 cgo 模块可以让 go 无缝调用 c 或者 c++ 的代码,而 python 本身就是个 c 库,自然也可以由 cgo 直接调用,前提是指定正确的编译条件,如 Python.h 头文件( ...

随机推荐

  1. B 树的简单认识

    理解 B 树的概念 B 树是一种自平衡的查找树,能够保持数据有序.这种数据结构能够让查找数据.顺序访问.插入数据及删除数据的动作,都能在对数时间内完成. 同一般的二叉查找树不同,B 树是一棵多路平衡查 ...

  2. go-zero微服务实战系列(四、CRUD热热身)

    上一篇文章我们把整个项目的架子搭建完成,服务在本地也已经能运行起来了,顺利成章的接下来我们就应该开始写业务逻辑代码了,但是单纯的写业务逻辑代码是比较枯燥的,业务逻辑的代码我会不断地补充到 lerbon ...

  3. 重学ES系列之拓展运算符

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 关于使用 koa路由与mysql模块, ctx.body获取不到值的问题

    var Koa = require('koa');var Router = require('koa-router' );var bodyParser = require('koa-bodyparse ...

  5. 第一章:Python的数据结构、函数和文件

    list list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: >>> classmates = ['Michael', ...

  6. SAP BDC 调用中 金额格式转换

    在BDC调用中,由于用户设置不同,导致金额.日期等字段的输入格式不正确.此处给出 自创 金额转换FM 并配有 调用方式. function zgm_conver_cuur. *"------ ...

  7. 数组基础篇(对应C++ Primer plus 4.10)

    概要:数组是由一组同类型的元素组成的集合,在内存上是一片连续的存储空间.C++提供了三种数组的表示方法:普通数组,模板类vector(C++98 新增的标准模板库STL提供该模板类)和模板类array ...

  8. 零基础学Java(4)字符串

    字符串 从概念上讲,Java字符串就是Unicode字符序列.例如,字符串"Java\u2122"由5个Unicode字符J.a.v.a和组成.Java没有内置的字符串类型,而是在 ...

  9. Mybatis-Generator 自定义注释

    继承DefaultCommentGenerator 或者CommentGenerator package com.zhianchen.mysqlremark.toword.config;import ...

  10. API 开发者需要避免的10个错误【翻译】

    随着低代码和无代码工具的出现,构建API比以往任何时候都更简单.更快.不过因为开发简单了,开发者很容易忽略一些潜在的问题,导致整个业务的下游影响. 在设计阶段多花点时间,可以确保API真正有用.安全. ...