Как вернуть struct из CGO

#struct #shared-libraries #cgo

#struct #разделяемые библиотеки #cgo

Вопрос:

Я хотел бы вызвать функцию Go из C, которая возвращает struct, и в C я хотел бы поработать над struct .

Цель состоит в том, чтобы заполнить структуру данными и вернуть информацию. Я не знаю, какой формат лучше всего передать. Мне нужно получить информацию в коде C о длине возвращаемых данных, а также о каждом str и l значении структуры.

(Функция, которую я вызываю, не strings.Split()) является . Это что-то более сложное, но мне нужны возвращаемые значения a char* и an 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, retvalue{str: C.CString(str), l: len(str)})
    }
    fmt.Printf("%#vn", 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("%dn",sv->count);
    char** cptr = sv->splitted;
    int *i = sv->lengths;
    for (size_t j = 0; j < sv->count; j  )
    {
        printf("%zu %s %dn",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
  

как и ожидалось.

Я не могу обещать, что это лучший способ и что ошибок нет, поэтому не принимайте это как ссылку.