I've been having a lot of fun with Go recently, and my usual first project when playing with a new language is to port remctl bindings to it.
In doing that with Go, I ran across a problem that had me puzzled for a while until I finally figured it out. In the remctl C binding there's a call remctl_commandv(struct remctl *r, const struct iovec *iov, size_t count); e.g. you pass remctl_commandv an array of iovec pointers and a count for how long that array is.
After quite a bit of confusion, I came around to doing this:
- Cgo allows you to reference C types directly, so I could create _Ctype_struct_iovec items
- Go has native arrays, but they're rarely used because they are rather inflexible. Typically you use slices instead. A slice is simply a pointer to some element of a fixed array (a slice can point to any element), and some housekeeping indicating the length of the slice and its capacity.
- With this, you can use the make function to create a slice: iov := make([]_Ctype_struct_iovec, len(cmd)) makes a slice of C iovecs long enough to hold all the things I need to pass.
for n, v := range cmd {
cmd_c := C.CString(v)
defer C.free(unsafe.Pointer(cmd_c))
iov[n].iov_base = unsafe.Pointer(cmd_c)
iov[n].iov_len = C.size_t(len(v))
}
if sent := C.remctl_commandv( r.ctx, &iov[0], C.size_t(len(cmd))); sent != 1 {
// It failed, return an error
return r.get_error()
}
We loop through cmd, doing the gymnastics we need to do to set each individual iov member. Then you pass the address of the 0th element of iov — with this trick, you're not referencing iov (which, remember, is a bit of housekeeping data that eventually points to the first bit of the array of _Ctype_struct_iovecs), but the actual underlying array.
I'll admit, this interface between Go and C makes me slightly queasy, but it works just fine. You can see the final project at the go-remctl GitHub page.