【问题标题】:CGO C# string array to GO sliceCGO C# 字符串数组到 GO 切片
【发布时间】:2023-02-24 04:12:19
【问题描述】:

我正在使用 CGO 从 GO 代码编译一个 C 库。然后从 C# 调用库函数。

在这个 GO 代码中,我有一个函数需要一个[]细绳输入,例如: func StringArray(strings []string)

我还有另一个函数需要一个[]整数输入,例如: func IntArray(vals []int)

如果我查看生成的头文件,我可以看到上述函数的以下内容:

extern __declspec(dllexport) void IntArray(GoSlice vals);
extern __declspec(dllexport) void StringArray(GoSlice strings);

通过创建以下结构,我可以从 C# 成功调用 IntArray 函数:

internal struct GoSlice
{
    public IntPtr data;
    public long len, cap;
    public GoSlice(IntPtr data, long len, long cap)
    {
        this.data = data;
        this.len = len;
        this.cap = cap;
    }
}

然后像这样调用函数:

long[] data = { 1, 2, 3, 4, 5, 6 };
IntPtr data_ptr = Marshal.AllocHGlobal(Buffer.ByteLength(data));
Marshal.Copy(data, 0, data_ptr, data.Length);
var nums = new GoSlice(data_ptr, data.Length, data.Length);
IntArray(nums);
Marshal.Copy(nums.data, data, 0, data.Length);

我也可以成功调用期望的函数细绳输入,通过创建以下结构:

internal struct GoString
{
    public string msg;
    public long len;
    public GoString(string msg, long len)
    {
        this.msg = msg;
        this.len = len;
    }
}

然后像这样调用函数:

string inputString = "Test";
GoString goString = new GoString(inputString, inputString.Length);

StringInput(goString);

我努力实现的是将预期的 []string GoSlice 传递给 StringArray 函数。有什么建议么?我需要 GoSlice 包含字符串而不是整数。

我已经尝试以各种方式将字符串而不是整数传递给 GoSlice,后者不适用于混合结果。我希望最终得到一个 []string GoSlice,它可以在从 C# 调用“CGO compiled”GO 函数时使用。

【问题讨论】:

    标签: c# go cgo


    【解决方案1】:

    当您在问题中提到 C#Go 类型时,我有点困惑,但我想我仍然可以为您解决这个问题。

    首先,cgo 创建一个 C 接口,因此您的问题被简化为:

    • 如何调用从 C# 获取数组的 C 函数
    • 如何从 GO 中的 cgo 接口接受 C 数组

    看来你对前者有很好的把握,所以我将专注于后者。

    对于具有动态结构的 C 函数,我们需要知道有多少内存以及它的布局。因此,接受字符串数组(包含与字符串切片相同类型的数据)的函数在 C 语言中可能如下所示

    int Parse22Strings(int argc, char** argv){
       if(argc!=2){
          return -1;
       }
       printf("string #1 %s string #2
    ",argv[0],argv[1]);
    }
    

    好的,如果我们想要使用 Go 的相同接口,我们只需要在 cgo 中匹配它(采用this其他答案的形式):

    func Parse22Strings(argc C.int, argv **C.char) {
    
        length := int(argc)
        tmpslice := (*[1 << 30]*C.char)(unsafe.Pointer(argv))[:length:length]
        gostrings := make([]string, length)
        for i, s := range tmpslice {
            gostrings[i] = C.GoString(s)
        }
    
        fmt.Printf("string #1 %s string #2
    ",gostrings[0],gostrings [1]);
    }
        
    

    所以此时你可以将上面的函数视为int Parse22Strings(int argc, char** argv),因为它就是这样调用的。如果您需要返回一个 Slice,您只需要再次将其转换为 C 类型:

    struct GoSliceSimple{
      int argc;
      char ** argv;
    };
    
    func Parse22Strings(argc C.int, argv **C.char) C.struct_GoSliceSimple {
    //be sure to use malloc so the Go garbage collector does not destroy any returned string
    }
    

    您可以对不透明类型使用更复杂的 can,但通常只是序列化 C 结构中的数据并来回传递它是最好的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-12
      • 2021-12-06
      • 2019-01-24
      • 2012-11-10
      • 1970-01-01
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多