I follow this tutorial
to write this code, in C:
#define Py_LIMITED_API
#include <Python.h>
PyObject * startVM(PyObject *, PyObject *);
int PyArg_ParseTuple_S(PyObject * args, char* a) {
return PyArg_ParseTuple(args, "s", &a);
}
static PyMethodDef FooMethods[] = {
{"startVM", startVM, METH_VARARGS, "Starts."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef foomodule = {
PyModuleDef_HEAD_INIT, "foo", NULL, -1, FooMethods
};
PyMODINIT_FUNC PyInit_foo(void) {
return PyModule_Create(&foomodule);
}
and this code in GO:
package main
import "fmt"
// #cgo pkg-config: python3
// #define Py_LIMITED_API
// #include <Python.h>
// int PyArg_ParseTuple_S(PyObject *,char *);
import "C"
//export startVM
func startVM(self, args *C.PyObject) {
var a *C.char
if C.PyArg_ParseTuple_S(args, a) == 0 {
//return nil
}
fmt.Println(a)
//return C.PyBytes_FromString(&a)
}
func main() {}
I can compile the code in go, but when I call in python the module with this command: python3 -c 'import foo; foo.startVM("hello")'
, it prints nil and results in segmentation fault...
Could someone know how to fix it?
Thanks in advance.
This function:
int PyArg_ParseTuple_S(PyObject * args, char* a) {
return PyArg_ParseTuple(args, "s", &a);
}
will only set the local copy of a
and won't return it into the calling function, because you pass the string pointer by value (by copying) so PyArg_ParseTuple
only sets the copy.
var a *C.char
C.PyArg_ParseTuple_S(args, a)
// Here `a` is not set, so it keeps its default value: nil.
You can solve this by passing pointer to your string instead of a string itself:
// C
int PyArg_ParseTuple_S(PyObject * args, char** a) {
return PyArg_ParseTuple(args, "s", a);
}
// Go
var a *C.char
if C.PyArg_ParseTuple_S(args, &a) == 0 {
//return nil
}
fmt.Println(a)
will print the address held by a
, and not the string, which it points to. Go has own type for strings and does not work with C strings.
If you want to print the text properly, you must convert it using C.GoString
:
// C string to Go string
func C.GoString(*C.char) string
(from https://golang.org/cmd/cgo/)
For example:
str := C.GoString(a)
fmt.Println(str)
I'm not familiar with python modules development, but I can assume, that the fault happens, because python method is expected to return a valid PyObject*
or NULL
. But you code does none of that. The return value of startVM
is not set and it doesn't default to nil, python accepts this non-nil pointer as a valid object and dereferences it, which causes segmentation error.
Specifying the return type of startVM
might help:
//export startVM
func startVM(self, args *C.PyObject) *C.PyObject {
// ...some code...
return nil
}