Golang中如何将[]byte转换为const char buf[][8]?

Golang中如何将[]byte转换为const char buf[][8]? 我编写了以下代码,通过cgo调用C函数void blsFunc(const char buf[][8])

/*
#cgo LDFLAGS:-lbls
#cgo linux,amd64 LDFLAGS:-L${SRCDIR}/lib/linux/amd64
#cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/lib/darwin/amd64
void blsFunc(const char buf[][8]);
*/
import "C"
import (
	"unsafe"
)

func BlsFunc(buf []byte) {
	C.blsFunc((*[8]C.char)(unsafe.Pointer(&buf[0])))
}

最小化的代码位于 https://github.com/herumi/test-travis-release。 在我的环境中,例如 Ubuntu 18.04.4 + Go1.13.4(以及1.14.1)、macOS 10.15.3 + Go1.13.4 等,go test -v ./bls 运行良好。 它在 Travis-CI + macOS + Go1.13.4 上也运行良好。

但是,它在 Travis-CI + Linux + Go1.13.4 上失败了。 https://travis-ci.org/github/herumi/test-travis-release/builds/667846719

bls/bls.go:15:118: cannot use _cgo0 (type *[8]_Ctype_char) as type unsafe.Pointer in argument to _Cfunc_blsFunc

这看起来像是一个语法错误,我不明白为什么尽管Go版本相同,但我的本地环境和Travis之间会存在差异。

我想弄清楚问题是我的代码还是Travis-CI。 你能给我一些建议吗?


更多关于Golang中如何将[]byte转换为const char buf[][8]?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

确实,测试样本使用 xgo 编译,针对 Darwin、Windows 和 Linux 目标平台,并生成了可正常工作的二进制文件。

更多关于Golang中如何将[]byte转换为const char buf[][8]?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


包装版本编译成功了!非常感谢。

🍻 干杯!

我还没有解决这个问题。 最小示例 https://github.com/herumi/test-travis-release/ 能在 Xgo 上运行吗?如果不能,我会检查一下。

我在使用 xgo 交叉编译器编译 bls-eth-go-binary 时遇到了相同的构建错误。

我添加了一个包装函数来避免在 Linux 上的 Travi-ci 出现此错误。 你可以尝试最新版本的 GitHub - herumi/bls-eth-go-binary 吗?

@herumi 你是否找到了这个问题的根源或者找到了解决方法?我在使用 xgo 交叉编译器编译 bls-eth-go-binary 时遇到了相同的构建错误。Xgo 容器中的 Go 版本是 1.13.4:https://github.com/karalabe/xgo

嗨,@herumi,我也没有从这个错误中看出什么。我建议向 Go 和/或 Travis-CI 团队咨询一下。

感谢您的评论。我会询问Travis-CI团队。

顺便提一下,通过检查 go tool cgo bls.go 的输出,我发现在 _obj/_cgo_gotypes.go 中,我本地的go环境和Travis上的go环境存在差异。

// 我的本地环境和Travis-mac的go

//go:cgo_unsafe_args
func _Cfunc_blsFunc(p0 *[8]_Ctype_char) (r1 _Ctype_void) {
    _cgo_runtime_cgocall(_cgo_4e5b00b212d7_Cfunc_blsFunc, uintptr(unsafe.Pointer(&p0)))
    if _Cgo_always_false {
        _Cgo_use(p0)
    }
    return
}

// Travis-linux的go

//go:cgo_unsafe_args
func _Cfunc_blsFunc(p0 unsafe.Pointer) (r1 _Ctype_void) {
    _cgo_runtime_cgocall(_cgo_4e5b00b212d7_Cfunc_blsFunc, uintptr(unsafe.Pointer(&p0)))
    if _Cgo_always_false {
        _Cgo_use(p0)
    }
    return
}

Travis-linux的go中,_Cfunc_blsFunc 的参数不是 p0 *[8]_Ctype_char 而是 p0 unsafe.Pointer。 这可能是导致错误的原因。 您能想到什么可能导致这种差异吗?

问题出在类型转换上。C函数期望的是const char buf[][8],即指向8个字符数组的指针,但你的Go代码传递的是*[8]C.char类型,这在某些环境下会被视为不兼容的类型。

正确的做法是将Go的[]byte转换为C的二维字符数组。以下是修正后的代码:

/*
#cgo LDFLAGS:-lbls
#cgo linux,amd64 LDFLAGS:-L${SRCDIR}/lib/linux/amd64
#cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/lib/darwin/amd64
void blsFunc(const char buf[][8]);
*/
import "C"
import (
    "unsafe"
)

func BlsFunc(buf []byte) {
    // 确保buf的长度是8的倍数
    if len(buf)%8 != 0 {
        panic("buf length must be multiple of 8")
    }
    
    // 计算二维数组的行数
    rows := len(buf) / 8
    
    // 创建C的二维数组指针
    cBuf := (*[1 << 30]C.char)(unsafe.Pointer(&buf[0]))
    
    // 调用C函数
    C.blsFunc((**C.char)(unsafe.Pointer(&cBuf[0])))
}

或者,如果C函数期望的是固定大小的二维数组,可以这样处理:

func BlsFunc(buf []byte) {
    if len(buf)%8 != 0 {
        panic("buf length must be multiple of 8")
    }
    
    // 将[]byte转换为[][]byte的8字节块
    rows := len(buf) / 8
    slices := make([][8]byte, rows)
    
    for i := 0; i < rows; i++ {
        copy(slices[i][:], buf[i*8:(i+1)*8])
    }
    
    // 传递第一个元素的地址
    C.blsFunc((*[8]C.char)(unsafe.Pointer(&slices[0])))
}

如果C函数需要动态数量的行,可以这样:

func BlsFunc(buf []byte) {
    if len(buf)%8 != 0 {
        panic("buf length must be multiple of 8")
    }
    
    rows := len(buf) / 8
    cArray := make([]*C.char, rows)
    
    for i := 0; i < rows; i++ {
        cArray[i] = (*C.char)(unsafe.Pointer(&buf[i*8]))
    }
    
    C.blsFunc((**C.char)(unsafe.Pointer(&cArray[0])))
}

Travis-CI上的错误可能是因为不同平台上的cgo类型检查严格程度不同。使用unsafe.Pointer进行适当的类型转换可以解决这个问题。

回到顶部