Can I define C functions that accept native Go types through CGo?

For the work that I am doing to integrate with an existing library, I had to write additional C code to provide an interface that could be used through CGo.

To avoid redundant copies of the data, I would like to be able to pass some standard Go types (like Go strings) to these C adapter functions.

I see that there are GoString and GoInterface types defined in the CGo header that are created for use by exported Go functions, but is there a way to use these types in my own prototypes of functions that CGo recognizes?

Currently, I used void * in C prototypes and passed unsafe.Pointer(&value) on the Go side. This is less clean than we would like (first, it gives C-code the ability to write a value).

Update:

Just to be clear, I know the difference between the original string type of Go and C char * . I want to say that since I will copy the string data passed to my C function, it doesn’t make sense in any case for the Go side code to make its own copy.

I also understand that the layout of the string may change in a future version of Go, and its size may vary by platform. But CGo already reveals type definitions that match the current platform for me through the documented header _cgo_export.h that it creates for me, so it seems a little strange to say that it is not defined:

 typedef struct { char *p; int n; } GoString; 

But there seems to be no way to use this definition in prototypes visible to CGo. I'm not too worried about binary compatibility, since the code using this definition will be part of my Go package, so it will be enough for compatibility with the source level (and there would be no big deal to upgrade the package if it wasn’t).

+4
source share
2 answers

Not really. You cannot safely mix, for example, Go ( string ) and C strings ( *char ) strings without using the provided helpers for this, i.e. GoString and CString . The reason is that in order to comply with the language specifications, a full copy of the contents of the line between the worlds of Go and C must be made. Moreover, the garbage collector must know what to read (Go lines) and what to ignore (C lines). And there’s something else to do, but let me keep it here.

Similar and / or other restrictions / problems apply to other types of the "magic" type Go, such as map or interface{} . In the case of interface types (but not only), it is important to understand that the internal implementation of interface{} (again, not only of this type) is not specified and implementation specific.

This is not only about the possible differences between, say gc and gccgo. It also means that your code will break at any time when the compiler developers decide to change some details of the (unspecified and therefore not guaranteed) implementation.

In addition, although Go does not use the (now) garbage collector, it can change without some kind of commit mechanism, any code that accesses the Go run time material will be doomed again.

Conclusion: pass only simple objects as arguments to C functions. POD structures with simple fields are also safe (pointer fields are usually absent). Of the complex Go types, use the provided helpers for Go strings; they exist for a (very good) reason.

+1
source

Passing a Go string to C is harder than it should be. Today there is no good way to do this. See https://golang.org/issue/6907 .

The best approach I know today is

 // typedef struct { const char *p; ptrdiff_t n; } gostring; // extern CFunc(gostring s); import "C" func GoFunc(s string) { C.CFunc(*(*C.gostring)(unsafe.Pointer(&s))) } 

This, of course, assumes that the Go representation of the string value does not change, which is not guaranteed.

0
source

Source: https://habr.com/ru/post/1481916/


All Articles