Converting between *gchar and *char with Cgo

802 views
Skip to first unread message

Dan Hackney

unread,
Aug 17, 2010, 12:20:45 PM8/17/10
to golang-nuts
I am currently working on a Go binding for GObject Introspection (so
that we don't have to keep writing the same bindings over and over ;),
and am a bit confused on type conversions when interfacing with Cgo. I
have just started writing Go, so it's entirely possible that I'm
missing something obvious ;)

Here is a short program demonstrating my problem:

==== show.go ====

package main

/*
#include <stdlib.h>
#include <girepository.h>


static gchar* to_gcharptr(char* s) { return (gchar*)s; }
*/
import "C"
import (
"unsafe"
"fmt"
)

func init() {
C.g_type_init()
}

func TypeLibPath(namespace string) string {
ns := C.to_gcharptr(C.CString(namespace))
defer C.free(unsafe.Pointer(ns))
cPath := C.g_irepository_get_typelib_path(nil, ns)
return C.GoString(cPath)
}


func main() {
path := TypeLibPath("Clutter")
fmt.Printf("typelib path: %#v\n", path)
}

==== end show.go ====

Note: the "girepository.h" header defines a function

const gchar* g_irepository_get_typelib_path(GIRepository *, const
gchar *)

And it has a simple Makefile:

==== Makefile ====

include $(GOROOT)/src/Make.$(GOARCH)

TARG=show
CGOFILES=\
show.go

CGO_CFLAGS=`pkg-config gobject-introspection-1.0 --cflags --libs`
CGO_LDFLAGS=`pkg-config gobject-introspection-1.0 --libs`

include $(GOROOT)/src/Make.pkg

==== end Makefile ====

The thing that is puzzling me is that when I try to 'make' this, I get
the following error:

==== Make output ====

$ make
CGOPKGPATH= /home/dhackney/bin/cgo -- `pkg-config gobject-
introspection-1.0 --cflags --libs` show.go
/home/dhackney/bin/8g -o _go_.8 show.cgo1.go _cgo_gotypes.go
show.go:16[show.cgo1.go:18]: cannot use cPath (type *_Ctypedef_gchar)
as type *_Ctype_char in function argument
make: *** [_go_.8] Error 1

==== end Make output ====

So C.GoString() doesn't want to accept a *_Ctypedef_gchar type.

The confusing thing for me is that in Glib, gchar is typedef'd to char
with:

typedef char gchar;

and in the generated _cgo_gotypes.go I see the line:

type _Ctypedef_gchar _Ctype_char

so I would expect that a function (like C.GoString()) which accepts a
*_Ctype_char would be fine with a *_Ctypedef_gchar. Shouldn't the
magic of Go's type system figure out that the two are the same and
accept a gchar*? Again, this could be my 1-day-long experience with Go
showing.

Similarly, I have a converter/caster for the other direction (char* to
gchar*) in the C function 'gchar* to_gcharptr(char*)' (copied from go-
gtk). It seems like this shouldn't be necessary, but doing this was
the only way to get the program to compile. Should I just solve the
former problem with a converter like this? It seemed like more typing
(both senses of the word) than should be necessary, but again, I'm new
at this ;)

Thanks!

bflm

unread,
Aug 17, 2010, 12:55:38 PM8/17/10
to golang-nuts
On Aug 17, 6:20 pm, Dan Hackney <d...@haxney.org> wrote:
> func TypeLibPath(namespace string) string {
>         ns := C.to_gcharptr(C.CString(namespace))
>         defer C.free(unsafe.Pointer(ns))
>         cPath := C.g_irepository_get_typelib_path(nil, ns)
>         return C.GoString(cPath)
>
> }
Not tested (rip from some old code), but I think this may work while
glib.h is imported (probably is included by girepository.h already in
your case):

func TypeLibPath(namespace string) string {
ns := (*C.gchar)(C.CString(namespace))
defer C.free(unsafe.Pointer(ns))
...
}

Russ Cox

unread,
Aug 17, 2010, 1:41:31 PM8/17/10
to Dan Hackney, golang-nuts
>  type _Ctypedef_gchar _Ctype_char
>
> so I would expect that a function (like C.GoString()) which accepts a
> *_Ctype_char would be fine with a *_Ctypedef_gchar. Shouldn't the
> magic of Go's type system figure out that the two are the same and
> accept a gchar*? Again, this could be my 1-day-long experience with Go
> showing.

There's no magic in Go's type system (that's a feature),
so *C.gchar and *C.char are different types. You should
be able to convert using (*C.gchar)(x) or (*C.char)(x).

Russ

Dan Hackney

unread,
Aug 19, 2010, 2:45:31 PM8/19/10
to golang-nuts
Thanks!

I think I had "magicness of types" confused with "automatically
satisfying an interface." After looking at this and more examples, I
think I am getting things straightened out. I also understand why a
non-"magic" type system is desirable.

Assuming I am understanding things correctly, could I modify the type
signature of the functions which take *_Ctypedef_gchar to take a
*GCharer type, defined as

type GCharer interface {
gchars() *C.gchar
}

and then define gchars() methods on C.gchar and C.char structs? I'm
curious to know whether there is a way to avoid having to sprinkle
(*C.gchar) and (*C.char) around my functions. I suppose it is more
efficient to perform a simple type conversion than call a function
each
time you are dealing with shuffling strings back and forth, but I
mainly
want to make sure I'm understanding the type and interface system
correctly.

Thanks again,

Daniel Hackney
Reply all
Reply to author
Forward
0 new messages