4

Library Code (Simplified Version):

// package1.go

package package1

import "C"

func Play(s *C.char) {
}

Client Code:

// main.go

package main

import "C"

import (
    "path/to/package1"
)

func PlayMore(s *C.char) {
    package1.Play(s)
}

func main() {
}

Build Error:

# command-line-arguments
main.go:12: cannot use s (type *C.char) as type *package1.C.char
            in argument to package1.Play

It seems that the "C" package is local to each package, the compiler treats them as different packages. I tried something like:

func PlayMore(s *package1.C.char) {
    package1.Play(s)
}

But then it is a syntax error.

Question:

  • How can I make the code compile?

More Information:

In the original problem, the parameter type is not *C.char. It is a pointer to a C type from a legacy library.

package1 has a low-level API and a high-level API:

  • The low-level API just wraps the C signatures with Go syntax. The parameter and return types are C.xxx types.
  • The high-level API provides a pure Go interface to the legacy library, which means there are no C.xxx parameter or return types. For instance, string is used instead of *C.char. The implementation prepares the parameters, and calls the low-level API for actual work.

The client code above (the main package), is in fact another package (package2) which is intended to be callable from C. You can treat it as a C library built on top of another C library, but the implementation is written in Go. For instance, PlayMore() above is exported in the original problem, via //export PlayMore, in order to be callable from C.

When package2 needs to call the functions provided by the legacy library, it calls the low-level API of package1. In fact, the reason why the low-level API of package1 is public is to allow packages like package2 to reuse it.

Related Questions:

Trouble using exported function type with C types as parameters

Go: Exporting functions with anonymous struct as a parameter [cannot use value (type struct {...}) as type struct {...} in argument to package.Func]

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189

2 Answers2

1

You may Export: type ExportedType C.char,like this working sample code:

package package1

import "C"
import "fmt"

type ExportedType C.char

func Play(s *ExportedType) {
    fmt.Println(C.GoString((*C.char)(s)))
}

main code:

package main

import "C"

import (
    "path/to/package1"
)

func PlayMore(s *C.char) {
    package1.Play((*package1.ExportedType)(s))
}

func main() {
    PlayMore(C.CString("Hi"))
}

output:

Hi
  • Thanks! But this doesn't seem to work if `C.char` is replaced by a pointer type. In the original problem, `C.char` above is in fact a pointer to an opaque type. – Siu Ching Pong -Asuka Kenji- Aug 18 '16 at 09:25
  • @siu-ching-pong-asuka-kenji Yes, use exactly in this style. this is tested and works fine. –  Aug 18 '16 at 09:27
  • What I mean is the code above is a simplified version of the actual code. In the original problem, it is not a `const char*` on the C side. It is a "pointer to pointer to an opaque type". By opaque type, it means the `struct` is declared but not defined, like this: `struct _otype; typedef struct _otype* otype; void play(otype* x);`. So, `x` is effectively "pointer to pointer to struct _otype`. In the original problem, function pointers are involved too, so things get some how complicated. It doesn't seem to work right now, but let me tweak it a little bit to see if it does. – Siu Ching Pong -Asuka Kenji- Aug 18 '16 at 10:09
  • Use `type ExportedType C.otype` like: `package package1 /* #include typedef struct _otype{ int8_t buf[16]; }otype; otype o = {0}; otype *p = &o; otype **q = &p; */ import "C" import "fmt" type ExportedType C.otype func Play(s **ExportedType) { fmt.Println("Hi") }` and `func main() { var t *package1.ExportedType p := &t package1.Play(p) }` –  Aug 18 '16 at 10:44
  • `otype` is declared by the legacy library, not by the Go wrapper I'm writing, so I have no control over it. The type is opaque, which means that the header from the legacy library doesn't define the size and layout of the `struct` --- thus it is not always 16 bytes. Using opaque types is the C way to make it "platform independent". The details of the `struct` is vendor specific. – Siu Ching Pong -Asuka Kenji- Aug 18 '16 at 10:54
  • I tried applying your solution to my project. It doesn't work. I found that only types as simple as `char` works. More complicated types don't work. The compiler complains that in `(*package1.ExportedType)(s)`, the conversion is not possible. – Siu Ching Pong -Asuka Kenji- Aug 18 '16 at 16:25
  • @SiuChingPong-AsukaKenji- So you may Ask new question about your original problem with more details. –  Aug 19 '16 at 05:11
  • In the original problem, the legacy library is a big one (Java Native Interface, aka JNI, to be exact). Most of the types, such as `jobject`, `jfieldID` and `jmethodID` are pointers to opaque types. Other types like `JNIEnv` and `JavaVM` contains function pointers, which I need to write a C wrapper first in order to call them. In the low level library, I wrote helper functions to convert C types and release them. The original code is available here: https://github.com/asukakenji/go/blob/master/jni/typeconv.go#L147-L151 Thank you very much for your help! – Siu Ching Pong -Asuka Kenji- Aug 19 '16 at 06:12
  • The low-level API code is available here: https://github.com/asukakenji/go/blob/master/jni/jnienv_low_level.go You can see that I need a C wrapper (the static functions) in order to invoke the function pointers contained in the `JNIEnv` struct. At https://github.com/asukakenji/go/blob/master/jni/typeconv.go#L147-L151 , the client needs to provide "a function accepting a `*C.char`". But since the `*C.char` type is not "cross-package", I may need to write it as `func WithCString(s string, f func(*ExportedType))`, which is not good looking. – Siu Ching Pong -Asuka Kenji- Aug 19 '16 at 06:17
0

It seems Go don't support export C type across packages. Related issue:

https://github.com/golang/go/issues/13467

A workaround is to pass unsafe.Pointer, and do a type cast inside this method.

Jiacai Liu
  • 2,623
  • 2
  • 22
  • 42