gpt4 book ai didi

带有共享库的 C# 字符串

转载 作者:行者123 更新时间:2023-12-01 21:11:51 26 4
gpt4 key购买 nike

我有一个用 Go 构建的共享库和一个 C# 程序,我想调用共享库中的函数。但打印一个空行。

这是 C# 代码:

using System.Runtime.InteropServices;

class Program
{
[DllImport("./main.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void Println(string s);

static void Main(string[] args)
{
Println("hello world");
}
}

这是 Go 代码:

package main

import "C"
import "fmt"

//export Println
func Println(s string){
fmt.Println(s)
}

func main(){}

如何将 C# 字符串作为 Go 字符串传递?

最佳答案

问题

问题是,虽然 -buildmode=c-shared (和 -buildmode=c-whatever 一般)确实使 Go 符号可以从 C 中调用,这在涉及字符串时会有所不同。

在 C 中,确实没有字符串这样的东西,但存在一个约定,即字符串是指向其第一个字节的指针,并且字符串的长度由代码为 0 的字节隐式定义(ASCII NUL)字符串。

在 Go 中,字符串是 struct s 的两个字段:指向包含字符串内容的内存块的指针和该 block 中的字节数。

可以看到,当 Go 工具集编译一个标记为“exported to C”的 Go 函数时,它基本上有两种选择:

  • 让函数“按原样”调用——要求调用者传递 Go 风格的 struct描述字符串的值。
  • 人为地将导出的函数“包装”到一个代码块中,该代码块将采用“C 风格”以 NUL 结尾的字符串,计算其中的字节数,烹制 Go 风格的 struct值,最后调用原始函数。

  • 对于非 Go 程序员来说,第二种方法可能更容易处理,但存在两个反对使用它的论点:
  • 这种方法会为每次调用带来隐藏成本:扫描字符串的字节——即使调用者知道它。
  • 正如@Selvin 在他们对该问题的评论中所建议的那样,如果需要的话,可以在 Go 代码中创建这样的包装器。

  • 因此,重申一下,当 Go 工具集编译在 c-whatever 中标记为“导出到 C”的 Go 函数时模式,它遵循 Go 约定,其工作结果是:
  • 编译后的库预计会收到 struct类型化的值由一个指针和一个大小组成——如上所述——用于 Go 类型的每个参数 string每个导出的函数。
  • 生成支持的 C 头文件,包含该 struct 的定义类型——它将被称为 GoString ,——以及所有导出函数的声明,使用 GoString类型为 string 的参数在 Go 方面。

  • 用更简单的话来说,如果你要创建一个文件 foo.go包含

    package main

    import "C"

    import "fmt"

    //export PrintIt
    func PrintIt(string s) {
    fmt.Println(s)
    }

    然后使用 go build -buildmode=c-shared -o foo.so foo.go 编译它,
    Go 工具集将创建 foo.sofoo.h , 包括——除其他外——类似:
    typedef struct { const char *p; ptrdiff_t n; } GoString;

    extern void PrintIt(GoString p0);

    如您所见,调用 PrintIt ,您应该将 GoString 的实例传递给它, 不是 const char * 类型的值.

    一个办法

    一个适当的解决方案是有类似的东西
    using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Sequential, Pack=0)]
    struct GoString {
    byte *p;
    int n;
    }

    进而
    [DllImport("./main.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    static extern void Println(GoString gs);

    请注意,我不太确定纯 int可以用于 ptrdiff_t在每种情况下——请自行研究在 .NET 互操作中应该正确使用什么。

    关于带有共享库的 C# 字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59602965/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com