CGO實戰專案中常用的資料轉換和使用

棋佈發表於2022-01-20

需要部署好相關環境和具備基本的知識點,這並非是一篇科普的文章,主要是針對實際專案中用到的型別轉換和使用,針對動態庫的函式呼叫引數傳遞和接收
1、GO環境,啟動支援CGO
2、事先安裝g++
3、看得懂GO和C的語法
4、最好會基本的makefile或者shell語法(表示我不懂,是個菜雞,只會看個大概)主要是自己除錯C需要用到
如果大家是一清二白的,請點選掃盲連結:chai2010.cn/advanced-go-programmin...

CGO實戰專案中常用的資料轉換和使用
由於GO支援 C語言的呼叫,所以只列出了和C的轉換,至於C++,需要轉換成C語言才可以成功呼叫。需要注意的是:每個C的變數都是限定在一個包內使用的,如果想跨包使用,請用GO封裝一層,否則會提示呼叫錯誤,找不到這個C變數。

1、字串引數的轉換

C的字串就是一個字元陣列的特例,簡單的說就是一個字元陣列以0結尾的陣列就是字串,所以不屬於基本資料型別 。

(1)、go的string轉換成C

C.CString是呼叫C的標準庫,申請了新的記憶體空間,需要呼叫C.free釋放,否則會記憶體洩漏。

    var  deviceIp string
    cdeviceIp := C.CString(deviceIp)
    defer C.free(unsafe.Pointer(cdeviceIp))
(2)、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 GopfRealDataCallBack
func 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))) }

本作品採用《CC 協議》,轉載必須註明作者和本文連結
歡迎轉載分享提意見,一起code

相關文章