Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/cgo: cannot create file using cgo function: file name becomes an invalid encoding #26376

Closed
prvst opened this issue Jul 13, 2018 · 6 comments

Comments

@prvst
Copy link

commented Jul 13, 2018

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go1.10.2 linux/amd64

Does this issue reproduce with the latest release?

not sure, didn't tested with anything above the version I have

What operating system and processor architecture are you using (go env)?

GOHOSTARCH="amd64"
GOHOSTOS="linux"

What did you do?

I'm trying to convert a GO program into a shared library to be used on my R script. At first I tested with something simple, I have a function that takes an input path (string) and creates a file on the disk. My problem is that the string with the path becomes something with an invalid encoding, see below:

If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.

This is my function:

import "C"

(...)

//export testWriter
func testWriter(path string) string {

	file, err := os.Create(path)
	if err != nil {
		return "error"
	}
	defer file.Close()

	return path
}

I compile the library with:

go build -o gowriter.so -buildmode=c-shared ./gowriter

And then I run my R script like this:

 Rscript -e 'dyn.load("/home/user/go/src/github.com/user/gowriter.so"); .Call("testWriter", "/home/user/test.txt")'

I also tried running this in a Python program:

from ctypes import *
lib = cdll.LoadLibrary("/home/user/go/src/github.com/prvst/testwriter.so")
lib.testReader("/home/user/test.txt")

And what I get is a file called ` � (invalid encoding) `. That's literally the file name, including that exclamation symbol you see there.

I added some print functions trying to understand the problem and this is what I get :

fmt.Println(reflect.TypeOf(path))                     # string
fmt.Println(reflect.TypeOf(C.CString(path)))   # *main._Ctype_char
fmt.Println(C.CString(path))                            # 0x559b609e1210

I also tried this without any success:

func testWriter(path *C.char) *C.char {

	file, err := os.Create(C.GoString(path))
	if err != nil {
		return C.CString("error")
	}
	defer file.Close()

	return path
}

What I find curious is that the function ends at the return statement and I can see on the terminal the correct string with the path that was passed to the function.

What did you expect to see?

An empty file on my disk called test.txt

What did you see instead?

An empty file on my disk called � (invalid encoding)

@ianlancetaylor ianlancetaylor changed the title Cannot create file using cgo function: file name becomes an invalid encoding cmd/cgo: cannot create file using cgo function: file name becomes an invalid encoding Jul 13, 2018

@ianlancetaylor ianlancetaylor added this to the Go1.12 milestone Jul 13, 2018

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 13, 2018

Using func testWriter(path string) string definitely won't work. You can only export a Go function that presents a C API, as documented at https://golang.org/cmd/cgo/#hdr-C_references_to_Go. I don't know what R and Python are passing when they pass a string, but I'm sure it's not a Go string type.

The func testWriter(path *C.char) *C.char variant has a chance of working, depending on how R and Python pass strings when calling a C function. What do you see with that version if you call fmt.Println(C.GoString(path))?

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 13, 2018

By the way, for questions about using Go, including using with other languages like R and Python, you are much more likely to get a better answer in a forum than on the issue tracker. See https://golang.org/wiki/Questions .

@prvst

This comment has been minimized.

Copy link
Author

commented Jul 13, 2018

Thanks for the fast reply.

This is the fmt.Println(C.GoString(path)) output:

just the symbol, nothing else.

About the forum; thanks I'll try that too. I tried Reddit without success.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 13, 2018

Trying doing the exact same thing with a C shared library and see what it gets for the string.

@prvst

This comment has been minimized.

Copy link
Author

commented Jul 18, 2018

@ianlancetaylor

I got a solution form a user on Reddit, here it is what he said:

Since you are passing a string in this example, it will be easier to use .C — which is for passing data as C data types — instead of .Call — which is for passing R objects.

According to the docs on "Foreign Function Interface", a string is passed as char **, so your function will take **C.char:

export testWriter
func testWriter(cPath **C.char) **C.char {
	// Convert the path to a go string
	path := C.GoString(*cPath)

	/* do stuff with path */

	return cPath
}

Using the function like that and the R .C method, I was able to correctly create a file using cmd/cgo.
I really appreciate your time in trying to help me, I think we can close the issue.

Thanks

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 18, 2018

Thanks for following up. Glad you got it working.

@golang golang locked and limited conversation to collaborators Jul 18, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
3 participants
You can’t perform that action at this time.