Data conversion and use commonly used in CGO practical projects

Posted by tekkenlord on Thu, 20 Jan 2022 17:36:41 +0100

preface

It is necessary to deploy the relevant environment and have basic knowledge points. This is not a popular science article. It is mainly aimed at the type conversion and use used in the actual project, and the function call parameter passing and receiving of the dynamic library
1. GO environment, start and support CGO
2. Prior installation g++
3. Understand the grammar of GO and C
4. It's best to know the basic makefile or shell syntax (it means I don't understand it. It's a vegetable chicken. I can only read it roughly). It's mainly needed for debugging C myself
If you are innocent, please click the literacy link: chai2010.cn/advanced-go-programmin...

List of basic data types


Because GO supports the call of C language, it only lists the conversion with C. as for C + +, it needs to be converted into C language to call successfully. It should be noted that each C variable is limited to one package. If you want to use it across packages, please encapsulate it with GO, otherwise you will be prompted with a call error and the C variable cannot be found.

Data type conversion used in the project

1. Conversion of string parameters

The string of C is a special case of a character array. In short, it is a character array. The array ending with 0 is a string, so it does not belong to the basic data type.

(1) Convert the string of go to C

C.CString is a standard library for calling C. It applies for new memory space and needs to call C.free to release it, otherwise it will leak memory.

    var  deviceIp string
    cdeviceIp := C.CString(deviceIp)
    defer C.free(unsafe.Pointer(cdeviceIp))
(2) Convert char * /char [] of C to string of go

Call C.GoString, the standard library of C. This function will not generate new memory space, create a copy, and will not free memory space.

Byte array of C to string of Go

For example, the type of C is BYTE sSerialNumber[SERIALNO_LEN];
The way to get is to use append to add bytes to the string

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

Note the difference between character arrays and strings mentioned earlier.

Go string to C character array

Type: CHAR szKeyFilePath[PU_CERT_FILE_PATH_MAX];

    var keyFilePath = "/home/docker/path/file.jpg"
    for i, b := range keyFilePath {
        szKeyFilePath[i] = C.CHAR(b)
    }

Consortium data acquisition

When receiving the data callback from the Huawei camera, the consortium type data is obtained. When it is obtained as an ordinary structure, the compiler will always prompt that the structure cannot be found. Later, it is necessary to obtain the consortium data in the C code, convert it to the basic data type, and then call Go again. Paste a code fragment to retrieve the data from the face recognition callback. You don't have to worry about the previous and subsequent text. Just look at the acquisition of data types.

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 ;
}

If you change this code to Go logic and read it directly in Go, you will be prompted that unMetaData cannot find the definition. Please let us know if there are other successful reading methods.

Call of callback function of C

1. First, Go code implements a function with the same data type, and uses / / export to export it as a C function. If it is found that the callback does not come in, first check whether the data type is correct, and then check whether the trigger conditions are met. This step is to receive the callback data of C language in Go language, that is, the data after callback is obtained in this function.
2. CGO calls C function. Some colleagues say that this step can be omitted. Just call the function in the first step in Go. I haven't tried it yet. That's what the company's ancestral code is written, so I can use it directly.
3. Just call it directly as a common function in GO language.
Look at the code example:
Function declaration of C:

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

Code of the first step:

//export GopfRealDataCallBack
func GopfRealDataCallBack(szBuffer *C.CHAR, lSize C.LONG, pUsrData unsafe.Pointer) {
    fmt.Println(szBuffer,lSize,pUsrData)
}

Step 2:

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

Step 3: C.pfRealDataCallBack(C.CGopfRealDataCallBack), which needs to be declared on import C, otherwise the call will not take effect

void * and unsafe Pointer

unsafe.Pointer claims to be a bridge for all data types. At the language level, the two can be considered equal. When you encounter void *, you can use unsafe Pointer to receive or pass. For the conversion of specific types, it needs to be forced according to the actual type. For example:

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

It depends on the actual situation. It is not omnipotent.

Transfer of structure array

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

struct_name is replaced by a specific structure name. The space is applied for release, and GO cannot detect the part of C.

Structure array traversal to obtain element data

    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 is replaced by the specific structure name. uintptr is the memory address of the element, and the element is obtained according to the offset. go for i := 0; i < int(resLen); i++ { result := (*C.DetectFaceResult)(unsafe.Pointer(uintptr(unsafe.Pointer(results)) + uintptr(i*C.sizeof_DetectFaceResult))) }

Topics: Go