如何从 CGO 返回结构

Posted

技术标签:

【中文标题】如何从 CGO 返回结构【英文标题】:How to return struct from CGO 【发布时间】:2020-10-28 14:23:13 【问题描述】:

我想从 C 中调用一个返回结构的 Go 函数,并且在 C 中我想处理该结构。

目标是用数据填充结构并返回信息。我不知道哪种格式最好通过。我需要在 C 代码中获取有关返回数据长度的信息,以及结构的每个 strl 值。

(我调用的函数不是strings.Split())。它更复杂,但我需要的返回值是char*int

我的 Go 源代码:

package main

import "C"
import (
    "fmt"
    "strings"
)


type retvalue struct 
    str *C.char
    l   int


// This is what I want to achieve (or something similar):
// func Split(input *C.char) []retvalue 
//    ...
//    ...
//    return ret
// 

//export Split
func Split(input *C.char) 
    all := strings.Split(C.GoString(input), ",")
    ret := []retvalue
    for _, str := range all 
        ret = append(ret, retvaluestr: C.CString(str), l: len(str))
    
    fmt.Printf("%#v\n", ret)
    // return ret


func main() 

这是我的 C 代码:

#include "libsplitter.h"

int main(int argc, char const *argv[])

    char* input="Hello,nice,world";
    Split(input);
    // do something with the return values from my Split()
    return 0;

我编译并运行代码(在 macOS 上)

go build -o libsplitter.dylib -buildmode=c-shared main.go
cc splitit.c -o splitit -lsplitter -L.
LD_LIBRARY_PATH=$PWD ./splitit

【问题讨论】:

【参考方案1】:

我找到了解决办法。

我的 C 文件:

#include "libsplitter.h"

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) 
    struct splitvalues* sv;
    sv = Split("hello,nice,world",",");

    printf("%d\n",sv->count);
    char** cptr = sv->splitted;
    int *i = sv->lengths;
    for (size_t j = 0; j < sv->count; j++)
    
        printf("%zu %s %d\n",j,*cptr,*i);
        cptr++;
        i++;
    

    free(sv->splitted);
    free(sv->lengths);
    return 0;

还有我的 Go 文件:

package main

/*

struct splitvalues 
    char** splitted;
    int* lengths;
    int count;
;

*/
import "C"

import (
    "strings"
    "unsafe"
)

//export Split
func Split(original *C.char, split *C.char) *C.struct_splitvalues 
    inputstring := C.GoString(original)
    splitstring := C.GoString(split)
    goResult := strings.Split(inputstring, splitstring)
    size := len(goResult)

    cLenghtsInt := C.malloc(C.size_t(size) * C.size_t(unsafe.Sizeof(C.int(0))))
    cSplittedStrings := C.malloc(C.size_t(size) * C.size_t(unsafe.Sizeof(uintptr(0))))
    returnStruct := (*C.struct_splitvalues)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_splitvalues))))

    ints := (*[1<<30 - 1]C.int)(cLenghtsInt)[:size:size]
    a := (*[1<<30 - 1]*C.char)(cSplittedStrings)

    for i, v := range goResult 
        ints[i] = C.int(len(v))
    

    for idx, substring := range goResult 
        a[idx] = C.CString(substring)
    

    returnStruct.splitted = (**C.char)(cSplittedStrings)
    returnStruct.lengths = (*C.int)(cLenghtsInt)
    returnStruct.count = C.int(len(goResult))

    return returnStruct

func main() 

输出是

3
0 hello 5
1 nice 4
2 world 5

正如预期的那样。

我不能保证这是最好的方法并且没有错误,所以不要以此作为参考。

【讨论】:

以上是关于如何从 CGO 返回结构的主要内容,如果未能解决你的问题,请参考以下文章

如何跨操作系统一致地将 C uint64_t 转换为 Cgo?

构建简单 cgo 模块的问题

如何从 C++ 中的函数返回结构?

如何从 mex 函数返回矩阵结构?

如何从线程返回结构?

如何从 SwiftUI 中的结构返回值?