Go / Cgo - How to access a field of a Cstruct? - go

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".

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.

Related

Typecasting structs with same underlying types

For basic types we can easily cast the types if their underlying types are same .But the fields inside struct with same memory layout cannot be easily cast from one to another type.
There is a proposal for this problem unfortunately it got rejected .After an hour of googling with no luck I came here seeking the help from experts.
Look at example below
:
package main
import (
"fmt"
)
type Int int
type A struct {
name string
age Int
}
type B struct {
name string
age int
}
func main() {
var a A= A{"Foo",21}
var b B= B{"Bar", 21}
fmt.Println(a,b,(A)(b)) //Error here as expected
}
Eventhough struct A and B has the same underlying type of struct { string,int} why cannot I cast to each other as the underlying type of Int is int.
Whether it is possible to cast recursively unless the underlying type differs?
You can't do it simply because the language spec does not allow it. Regarding structs, you can only convert from one type to the other if Spec: Conversion:
A non-constant value x can be converted to type T in any of these cases:
...
ignoring struct tags (see below), x's type and T have identical underlying types.
If you're absolutely sure the structs' memory layout is identical, you may use an unsafe conversion (using package unsafe) like this:
var a A = A{"Foo", 21}
var b B
b = *(*B)(unsafe.Pointer(&a))
fmt.Println(a, b)
This will output (try it on the Go Playground):
{Foo 21} {Foo 21}
But use this as the last resort. Using unsafe you lose compile time type safety and portability guarantees. E.g. if later you only modify one of the structs, the above code will continue to compile even though it might not be correct anymore, and the compiler will not able to inform you about that.

How to get a pointer to the underlying value of an Interface{} in Go

I'm interfacing with C code in Go using cgo, and I need to call a C function with a pointer to the underlying value in an Interface{} object. The value will be any of the atomic primitive types (not including complex64/complex128), or string.
I was hoping I'd be able to do something like this to get the address of ptr as an unsafe.Pointer:
unsafe.Pointer(reflect.ValueOf(ptr).UnsafeAddr())
But this results in a panic due to the value being unaddressable.
A similar question to this is Take address of value inside an interface, but this question is different, as in this case it is known that the value will always be one of the types specified above (which will be at most 64 bits), and I only need to give this value to a C function. Note that there are multiple C functions, and the one that will be called varies based off of a different unrelated parameter.
I also tried to solve this using a type switch statement, however I found myself unable to get the address of the values even after the type assertion was done. I was able to assign the values to temporary copies, then get the address of those copies, but I'd rather avoid making these copies if possible.
interface{} has own struct:
type eface struct {
typ *rtype
val unsafe.Pointer
}
You have no access to rtype directly or by linking, on the other hand, even though you'll copy whole rtype, it may be changed (deprecated) at future.
But thing is that you can replace pointer types with unsafe.Pointer (it may be anything else with same size, but pointer is much idiomatic, because each type has own pointer):
type eface struct {
typ, val unsafe.Pointer
}
So, now we can get value contained in eface:
func some_func(arg interface{}) {
passed_value := (*eface)(unsafe.Pointer(&arg)).val
*(*byte)(passed_value) = 'b'
}
some_var := byte('a')
fmt.Println(string(some_var)) // 'a'
some_func(some_var)
fmt.Println(string(some_var)) // 'a', it didn't changed, just because it was copied
some_func(&some_var)
fmt.Println(string(some_var)) // 'b'
You also might see some more usages at my repo:
https://github.com/LaevusDexter/fast-cast
Sorry for my poor English.

confusion in understanding type conversions in go

package main
import (
"fmt"
)
type val []byte
func main() {
var a []byte = []byte{0x01,0x02}
var b val = a
fmt.Println(a)
fmt.Println(b)
}
o/p:
[1 2]
[1 2]
Here, my understanding is that a,b identifier share the same underlying type([]byte). so we can exchange the values b/w 2 variables.
package main
import (
"fmt"
)
type abc string
func main() {
fm := fmt.Println
var second = "whowww"
var third abc = second //compile Error at this line 12
fm(second)
fm(third)
}
In line 12 I'm not able to assign the variable.
This Error can be eliminated by using Explicit conversion T(x), I want to understand why we cannot do implicit conversion
As both variables share the same underlying-type, but I'm not able to assign it.
can someone explain the reason behind these?
IF possible provide me the good documentation for type conversions between variables, struct types, function parameters.
This is by design. The Go programming language requires assignment between different types to have an explicit conversion.
It might look like you're simply aliasing the string type to have a different name, but you're technically creating a new type, with a storage type of string, there's a subtle difference.
The way you would define an alias in Go (as of 1.9) is subtly different, theres an equals sign.
type abc = string
If there's any confusion as to why Go doesn't have implicit conversions, it might seem silly when you're only dealing with an underlying string type, but with more complex types, it ensures that the programmer knows just by looking at the code that a conversion is happening.
Its especially helpful in debugging an application, particularly when converting between numeric types to know when a conversion is taking place, so that if there is a truncation of bits i.e. uint64 to uint32, it is obvious to see where that is happening.
https://tour.golang.org/basics/13

Go: Pointer to type is incompatible with interface

I'm not sure how to formulate the question properly. I have 2 go code snippets that should do the exact same thing but apparently they don't, since one works and the other doesn't (doesn't compile)
func writeSomething(writer *io.Writer) {
}
func main() {
file, _ := os.Create("error.log")
var logWriter io.Writer = file
writeSomething(&logWriter)
}
func main2() {
file, _ := os.Create("error.log")
writeSomething(&file)
}
Playground
main() works and main2() doesn't.
prog.go:20:17: cannot use &file (type **os.File) as type *io.Writer in argument to writeSomething:
*io.Writer is pointer to interface, not interface
The only difference is that I used an intermediate variable but I'm not doing any pointer referencing or dereferencing.
What am I doing wrong here?
You shouldn't use pointers to interfaces, it is a bad practice in Go.
And about code that doesn't compile - when you create an intermediate variable logWriter you allocate an interface variable, that has its own allocated memory to describe what it stores (actual type) and a reference to the actual structure.
That is why &logWriter and &file have different types.
But as I sad previously, you should not use pointers to interfaces, just use interface type and do not get address of interface type variable.

Type assertion failed when using two different (but identical) types

I have this code
package main
import "fmt"
type MyType int
func main() {
var i interface{} = 12
f := i.(MyType)
fmt.Println(f)
}
However, I get this error:
panic: interface conversion: interface is int, not main.MyType
However, int is, in this case, the same as MyType. Is there any way to do this without using the same type?
They're not identical types. As the runtime tells you, one is int and one is MyType. Go has a very specific definition of identical.
Straight from the spec:
A type declaration binds an identifier, the type name, to a new type
that has the same underlying type as an existing type, and operations
defined for the existing type are also defined for the new type. The
new type is different from the existing type.
https://golang.org/ref/spec#Type_identity
https://golang.org/ref/spec#Type_declarations
https://golang.org/ref/spec#Types
You can easily convert between the two, MyType(12) works just fine, but type assertion is different from converting: https://golang.org/ref/spec#Type_assertions
If you'd like to do some reading about interfaces and types and all of that fun stuff, these are both super helpful:
http://blog.golang.org/laws-of-reflection
http://research.swtch.com/interfaces

Resources