英文:
Go / Cgo - How to access a field of a Cstruct?
问题
我正在开发一个使用Go语言编写的应用程序,用于将音频文件从一种格式转码为另一种格式:
我使用了goav库,该库使用Cgo来绑定FFmpeg的C库:
https://github.com/giorgisio/goav/
goav库中的package avformat
有一个typedef,用于将原始的FFmpeg lib C-Struct AVOutputFormat进行类型转换:
type (
OutputFormat C.struct_AVOutputFormat
)
在我的代码中,我有一个名为outputF
的变量,类型为OutputFormat
,它是一个C.struct_AVOutputFormat
。
真正的C
中的AVOutputFormat
结构体有以下字段:
name, long_name, mime_type, extensions, audio_codec, video_codec, subtitle_codec,..
还有许多其他字段。
参考:https://ffmpeg.org/doxygen/2.6/structAVOutputFormat.html
我通过fmt.Println(outputF)
验证了情况,并得到了以下结果:
{0x7ffff7f23383 0x7ffff7f23907 0x7ffff7f13c33 0x7ffff7f23383 86017 61 0 128 <nil> 0x7ffff7f8cfa0 <nil> 3344 0x7ffff7e3ec10 0x7ffff7e3f410 0x7ffff7e3ecc0 <nil> 0x7ffff7e3dfc0 <nil> <nil> <nil> <nil> <nil> <nil> 0 0x7ffff7e3e070 0x7ffff7e3e020 <nil>}
音频编解码器字段位于位置5
,包含86017
我使用reflect
包验证了字段名称:
val := reflect.Indirect(reflect.ValueOf(outputF))
fmt.Println(val)
fmt.Println("Fieldname: ", val.Type().Field(4).Name)
输出:
Fieldname: audio_codec
我尝试访问原始AVOutputFormat
的字段audio_codec
,使用了以下代码:
fmt.Println(outputF.audio_codec)
错误:outputF.audio_codec未定义(无法引用未公开的字段或方法audio_codec)
fmt.Println(outputF._audio_codec)
错误:outputF._audio_codec未定义(类型*avformat.OutputFormat没有字段或方法_audio_codec)
根据我在Cgo文档中的阅读:
在Go文件中,如果C的结构体字段名称在Go中是关键字,可以在其前面加上下划线来访问它们:如果x指向一个具有名为"type"的字段的C结构体,x._type可以访问该字段。无法在Go中表示的C结构体字段,例如位字段或不对齐的数据,在Go结构体中被省略,用适当的填充替换以达到下一个字段或结构体末尾。
但是我不知道我做错了什么。
编辑:
好吧,肯定不需要下划线,因为audio_codec在Go中不是关键字。这一点我现在明白了。但是仍然有一个问题,为什么我无法访问C结构体字段"audio_codec"。
英文:
I develope an application in Go for transcode an audio file from one format to another one:
I use the goav library that use Cgo to bind the FFmpeg C-libs:
https://github.com/giorgisio/goav/
The goav library; package avformat
has a typedef that cast the original FFmpeg lib C-Struct AVOutputFormat:
type (
OutputFormat C.struct_AVOutputFormat
)
In my code i have a variable called outputF
of the type OutputFormat
that is a C.struct_AVOutputFormat
.
The C
real AVOutputFormat
struct has fields:
name, long_name, mime_type, extensions, audio_codec, video_codec, subtitle_codec,..
and many fields more.
See: https://ffmpeg.org/doxygen/2.6/structAVOutputFormat.html
I verified the situation by fmt.Println(outputF)
and reached:
{0x7ffff7f23383 0x7ffff7f23907 0x7ffff7f13c33 0x7ffff7f23383 86017 61 0 128 <nil> 0x7ffff7f8cfa0 <nil> 3344 0x7ffff7e3ec10 0x7ffff7e3f410 0x7ffff7e3ecc0 <nil> 0x7ffff7e3dfc0 <nil> <nil> <nil> <nil> <nil> <nil> 0 0x7ffff7e3e070 0x7ffff7e3e020 <nil>}
The audio codec field is on position 5
and contains 86017
I verified the field name by using the package reflect
:
val := reflect.Indirect(reflect.ValueOf(outputF))
fmt.Println(val)
fmt.Println("Fieldname: ", val.Type().Field(4).Name)
Output:
Fieldname: audio_codec
I try to access the field audio_codec
of the original AVOutputFormat
using:
fmt.Println(outputF.audio_codec)
ERROR: outputF.audio_codec undefined (cannot refer to unexported field or method audio_codec)
fmt.Println(outputF._audio_codec)
ERROR: outputF._audio_codec undefined (type *avformat.OutputFormat has no field or method _audio_codec)
>As i read in the Cgo documentation:
Within the Go file, C's struct field names that are keywords in Go can be accessed by prefixing them with an underscore: if x points at a C struct with a field named "type", x._type accesses the field. C struct fields that cannot be expressed in Go, such as bit fields or misaligned data, are omitted in the Go struct, replaced by appropriate padding to reach the next field or the end of the struct.
But I have no idea what im doing wrong.
Edit:
Okay for sure no underscore is required as audio_codec is not a keyword in Go. This i understood for now. But still there is the question why im not able to access the CStruct field "audio_codec".
答案1
得分: 2
这里涉及到GO/CGO的一些特殊性:
type OutputFormat C.struct_AVOutputFormat
是一个go的类型声明,而不是一个别名。可以将其视为一个薄包装而不是别名。因此,OutputFormat != C.struct_AVOutputFormat
,并且C.struct_AVOutputFormat的字段没有被导出,这就是为什么会出现ERROR: outputF.audio_codec undefined (cannot refer to unexported field or method audio_codec)
的原因。
如果字段被称为Audio_codec,它将符合go的导出标识符的定义,我们可以访问它,但实际上并不是这样。
有一种方法可以解决这个问题,但我建议在继续之前三思而后行,因为它使用了不安全指针,并且存在程序在运行时可能失去可移植性和/或稳定性的风险。这是一个关于不安全指针的好介绍,如果你想了解更多。
现在,如果你确实确定要这样做,解决方案是将指向OutputFormat
的指针转换为不安全指针,然后将其转换为指向C.struct_AVOutputFormat
的指针。请注意,这需要加载FFMPEG头文件以获取C.struct_AVOutputFormat
的定义。
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import (
"fmt"
"unsafe"
"github.com/giorgisio/goav/avformat"
)
func getOutput() *avformat.OutputFormat {
// 调用avformat并获取一个OutputFormat返回
}
func main() {
outputF := getOutput()
coutput := *(*C.struct_AVOutputFormat)(unsafe.Pointer(outputF))
fmt.Println(coutput.audio_codec) // 现在应该可以工作了
}
注意:我没有测试cgo包配置和<libavformat/avformat.h>
导入是否正确,但这在我用一个简单的C库进行尝试时是有效的。
英文:
There are some idiosyncrasies to GO/CGO that you're bumping into here:
type OutputFormat C.struct_AVOutputFormat
is a go type declaration, not an alias. It might help to think of it as a thin wrapper rather than an alias. Therefore OutputFormat != C.struct_AVOutputFormat
and the fields of C.struct_AVOutputFormat are not exported which is why you're getting the ERROR: outputF.audio_codec undefined (cannot refer to unexported field or method audio_codec)
If the field was called Audio_codec it would fit go's definition of an exported identifier and we could access it, but it isn't.
There is a way to get around this, but I'd recommend thinking twice before going ahead with it as it uses unsafe pointers and there are risks that your program may lose portability and/or stability at runtime. This is a good intro to unsafe pointers if you'd like to learn more.
Now if you're really sure you want to do this, the solution is to convert the pointer to OutputFormat
into an unsafe pointer then convert it into a pointer to C.struct_AVOutputFormat
. Note that this requires you to load the FFMPEG headers to get the definition of C.struct_AVOutputFormat
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import (
"fmt"
"unsafe"
"github.com/giorgisio/goav/avformat"
)
func getOutput() *avformat.OutputFormat {
// calls out to avformat and gets an OutputFormat back
}
func main() {
outputF := getOutput()
coutput := *(*C.struct_AVOutputFormat)(unsafe.Pointer(outputF))
fmt.Println(coutput.audio_codec) // This should now work
}
Caveat: I've not tested that the cgo package config and <libavformat/avformat.h>
import are correct but this worked with a simple C library I stood up to try it out with.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论