首页后端开发其他后端知识CGO中怎样实现数据转换及如何使用

CGO中怎样实现数据转换及如何使用

时间2024-03-28 06:34:03发布访客分类其他后端知识浏览1623
导读:这篇文章给大家分享的是“CGO中怎样实现数据转换及如何使用”,文中的讲解内容简单清晰,对大家认识和了解都有一定的帮助,对此感兴趣的朋友,接下来就跟随小编一起了解一下“CGO中怎样实现数据转换及如何使用”吧。...
这篇文章给大家分享的是“CGO中怎样实现数据转换及如何使用”,文中的讲解内容简单清晰,对大家认识和了解都有一定的帮助,对此感兴趣的朋友,接下来就跟随小编一起了解一下“CGO中怎样实现数据转换及如何使用”吧。

 


前言

需要部署好相关环境和具备基本的知识点,这并非是一篇科普的文章,主要是针对实际项目中用到的类型转换和使用,针对动态库的函数调用参数传递和接收
1、GO环境,启动支持CGO
2、事先安装g++
3、看得懂GO和C的语法
4、最好会基本的makefile或者shell语法(表示我不懂,是个菜鸡,只会看个大概)主要是自己调试C需要用到

基本数据类型一览

由于GO支持 C语言的调用,所以只列出了和C的转换,至于C++,需要转换成C语言才可以成功调用。需要注意的是:每个C的变量都是限定在一个包内使用的,如果想跨包使用,请用GO封装一层,否则会提示调用错误,找不到这个C变量。

项目中用到的数据类型转换

go的string转换成C

C的字符串就是一个字符数组的特例,简单的说就是一个字符数组以0结尾的数组就是字符串,所以不属于基本数据类型 。
C.CString是调用C的标准库,申请了新的内存空间,需要调用C.free释放,否则会内存泄漏。

    var  deviceIp string
    cdeviceIp := C.CString(deviceIp)
    defer C.free(unsafe.Pointer(cdeviceIp))
C的char * /char[] 转换成go的string

调用C的标准库 C.GoString,这个函数不会产生新的内存空间,创建的是一个副本,也不会释放内存空间。

C的字节数组转Go的string

比方说C的类型是:BYTE sSerialNumber[SERIALNO_LEN];
获取的方式就是利用append添加字节到字符串

    serialNo := make([]byte, 0)
    for _, v := range sSerialNumber {

        if v != 0 {

            serialNo = append(serialNo, byte(v))
        }

    }
    

注意前面提到的字符数组和字符串的区别。

Go的string转C的字符数组

类型:CHAR szKeyFilePath[PU_CERT_FILE_PATH_MAX];

    var keyFilePath = "/home/docker/path/file.jpg"
    for i, b := range keyFilePath {

        szKeyFilePath[i] = C.CHAR(b)
    }

联合体的数据获取

接华为摄像头的数据回调的时候有联合体类型数据的获取,当作普通结构体获取的时候编译会一直提示找不到这个结构体,后面不得已,在C代码里面获取到联合体的数据之后,转换成基本数据类型,再重新Go调用。贴一个代码片断,人脸识别回调获取的数据。不用纠结前后文,看数据类型的获取就好。

void CGopfFaceSnapCallBack(CHAR *szBuffer, LONG lSize, void *pUsrData) {
    
    PU_META_DATA *pstMetaData = 0;
    
    int ret = Wrapper_IVS_User_GetMetaData(szBuffer, lSize, TARGET, &
    pstMetaData);

    if (ret == PU_FALSE ){
    
        return ;

    }
    
    PU_UserData *pstMetaUserData = pstMetaData->
    pstMetaUserData;

    char  name[100]={
0}
    ;

    char  cardID[100]={
0}
    ;
    
    for(UINT uIndex = 0;
     uIndex  pstMetaData->
    usValidNumber;
 ++uIndex){
    
       //printf("pstMetaData eType : %x\n", pstMetaUserData[uIndex].eType);

        if (pstMetaUserData[uIndex].eType == FACE_INFO){
    
            strcpy(cardID, pstMetaUserData[uIndex].unMetaData.stFaceInfo.cardID);
    
            strcpy(name, pstMetaUserData[uIndex].unMetaData.stFaceInfo.name);
    
            printf("GopfFaceSnapCallBack unMetaData.stFaceInfo cardID : %s\n", pstMetaUserData[uIndex].unMetaData.stFaceInfo.cardID);
    
            printf("GopfFaceSnapCallBack unMetaData.stFaceInfo name : %s\n", pstMetaUserData[uIndex].unMetaData.stFaceInfo.name);
    
            GopfFaceSnapCallBack(pstMetaUserData[uIndex].unMetaData.stFaceInfo.cardID,pUsrData);
    
            break ;

        }

    }
    
    Wrapper_IVS_User_FreeMetaData(&
    pstMetaData);
    
    return ;
}
    

如果这段代码换成Go的逻辑,直接在Go里面去读取的话会提示unMetaData 找不到定义。有其他成功读取方式的,还请告知。

C的回调函数的调用

1、先Go代码实现数据类型一致的函数,利用//export 导出为C函数,如果发现回调没进来,首先检查一下数据类型是否正确,再检查触发条件是否满足。这一步是为了在Go语言里面接收到C语言的回调数据,也就是回调后的数据是在这个函数里面获取。
2、CGO调用C函数,有同事说这一步可以不用,直接在Go里面调用第一步的函数就可以,我还没试过,公司祖传的代码就是这么写的,也就直接照用了。
3、在GO语言里面当作常用函数直接调用就好。
看代码示例:
C的函数声明:

typedef VOID (CALLBACK *pfRealDataCallBack)(CHAR *szBuffer, LONG lSize, VOID *pUsrData);

第一步的代码:

//export GopfRealDataCallBackfunc GopfRealDataCallBack(szBuffer *C.CHAR, lSize C.LONG, pUsrData unsafe.Pointer) {

    fmt.Println(szBuffer,lSize,pUsrData)}
    

第二步:

extern void GopfRealDataCallBack(CHAR *szBuffer, LONG lSize, void *pUsrData);
void CGopfRealDataCallBack(CHAR *szBuffer, LONG lSize, void *pUsrData){
    
    return GopfRealDataCallBack(szBuffer,lSize,pUsrData);
}
    

第三步:C.pfRealDataCallBack(C.CGopfRealDataCallBack),需要在import C 上声明,否则调用不生效

void*和unsafe.Pointer

unsafe.Pointer号称是所有数据类型的转接桥梁,在语言层面两个可以认为等同的,当碰到void*可以用unsafe.Pointer来接收或者传递,具体类型的转换,需要根据实际类型做强转。比方说:

lpOutBuff := unsafe.Pointer(C.malloc(1024))

这个1024看实际情况修改,不是万能的。

结构体数组的传递

results := (*C.struct_name)(C.malloc(C.size_t(C.sizeof_struct_name * C.int(resLen))))
    defer C.free(unsafe.Pointer(results))

struct_name换成具体的结构体名称,申请了空间要释放,GO检测不到C的部分。

结构体数组遍历获取元素数据

    for i := 0;
     i  int(resLen);
 i++ {

        result := (*C.struct_name)(unsafe.Pointer(uintptr(unsafe.Pointer(results)) + uintptr(i*C.sizeof_struct_name)))
    }
    

struct_name换成具体的结构体名称,uintptr是元素内存地址,根据偏移量获取元素。go for i := 0; i int(resLen); i++ { result := (*C.DetectFaceResult)(unsafe.Pointer(uintptr(unsafe.Pointer(results)) + uintptr(i*C.sizeof_DetectFaceResult))) }


通过以上内容的阐述,相信大家对“CGO中怎样实现数据转换及如何使用”已经有了进一步的了解,更多相关的问题,欢迎关注网络或到官网咨询客服。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: CGO中怎样实现数据转换及如何使用
本文地址: https://pptw.com/jishu/654762.html
Node.js中的事件循环怎样理解,原理是什么 GO语言中实现类型转换的方法是什么

游客 回复需填写必要信息