using cgo I am calling c function. I ran into situation where I have to copy C.int address into C.char[4]. Can I do that in go?
code snippet C- Structure:
struct data
{
char *a_add;
unsigned int length;
}
Go-Code
func main() {
var d[3] C.data
var filedescriptor C.int
d[0].a_add = &filedescriptor
d[0].length = 4
}
The problem is a_add is a char*. But I need to pass int variable address. The c-code is a legacy code, and I can't fix datatype now. Other C modules uses it, and it's working in C with a warning. where as in go, it is error.
Is there any way that I can copy address of int variable into char* array in cgo.
Update: I tried d[0].a_add = (*C.char)(unsafe.Pointer(&filedescriptor )),
getting Error: panic: runtime error: cgo argument has Go pointer to Go pointer
What Am I missing?
One of the problems you are running into is that in a call into C code, you may not pass a pointer to a Go pointer. The variable filedescriptor
is a C.int
, but &filedescriptor
is a Go pointer, so you cannot use that (or rather, you cannot use that in the a_add
field as a value).
There is a great deal about your C code that is not clear to me, but you can probably use the code below. Note that this code may be overkill for your particular situation. It is not meant to be particularly efficient or good, just as clear as possible while being extremely flexible—for instance, it can read from and write to packed C structures.
package main
// #include <stdio.h>
// #include <stdlib.h>
// #include <string.h>
//
// struct data {
// char *a_add;
// unsigned int length;
// };
//
// void f(struct data *p) {
// printf("p->a_add = %p, p->length = %u\n", p->a_add, p->length);
// printf("p->a_add as an int: %d\n", *(int *)p->a_add);
// *(int *)p->a_add = 0x12345678;
// }
import "C"
import (
"fmt"
"unsafe"
)
const cIntSize = C.sizeof_int
// Produce a Go int64 from a C int. The caller passes the address
// of the C int.
func int64FromCInt(ci unsafe.Pointer) int64 {
// Get a slice pointing to the bytes of the C int.
sci := (*[cIntSize]byte)(ci)[:]
switch {
case cIntSize == unsafe.Sizeof(int64(0)):
var gi int64
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return gi
case cIntSize == unsafe.Sizeof(int32(0)):
var gi int32
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return int64(gi)
case cIntSize == unsafe.Sizeof(int(0)):
var gi int
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return int64(gi)
default:
panic("no Go integer size matches C integer size")
}
}
// Write C int (via an unsafe.Pointer) from Go int. The caller
// passes the address of the C int.
func writeCIntFromInt(gi int, ci unsafe.Pointer) {
// Get a slices covering the bytes of the C int.
sci := (*[cIntSize]byte)(ci)[:]
switch {
case cIntSize == unsafe.Sizeof(gi):
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sci, sgi)
case cIntSize == unsafe.Sizeof(int64(0)):
// Copy value to int64 for copying purposes.
// Since int64 holds all int values, this always works.
gi2 := int64(gi)
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
copy(sci, sgi)
case cIntSize == unsafe.Sizeof(int32(0)):
// Copy value to int32 for copying purposes.
// Panic if we destroy the value via truncation.
gi2 := int32(gi)
if int(gi2) != gi {
panic(fmt.Sprintf("unable to send Go value %x to C: size of Go int=%d, size of C int=%d", gi, unsafe.Sizeof(gi), cIntSize))
}
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
copy(sci, sgi)
default:
panic("no Go integer size matches C integer size")
}
}
func main() {
b := C.malloc(cIntSize)
defer C.free(b)
writeCIntFromInt(32767, b)
d := C.struct_data{a_add: (*C.char)(b), length: cIntSize}
fmt.Println("calling C.f(d)")
C.f(&d)
result := int64FromCInt(unsafe.Pointer(d.a_add))
fmt.Printf("result = %#x\n", result)
}