英文:
Python module written in Golang and C
问题
我按照这个教程编写了以下代码,使用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);
}
以及使用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() {}
我可以编译Go代码,但是当我在Python中使用以下命令调用模块时:python3 -c 'import foo; foo.startVM("hello")'
,它打印出nil并导致段错误...有人知道如何修复吗?提前感谢。
英文:
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.
答案1
得分: 5
翻译结果:
空输出
这个函数:
int PyArg_ParseTuple_S(PyObject * args, char* a) {
return PyArg_ParseTuple(args, "s", &a);
}
只会设置a
的本地副本,并且不会将其返回给调用函数,因为你通过值(通过复制)传递了字符串指针,所以PyArg_ParseTuple
只设置了副本。
var a *C.char
C.PyArg_ParseTuple_S(args, a)
// 这里`a`没有被设置,所以它保持默认值:nil。
你可以通过传递指向字符串的指针来解决这个问题,而不是传递字符串本身:
// 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 {
// 返回nil
}
正确打印
fmt.Println(a)
会打印a
所持有的地址,而不是它指向的字符串。Go有自己的字符串类型,不适用于C字符串。
如果你想正确打印文本,你必须使用C.GoString
进行转换:
// C字符串到Go字符串
func C.GoString(*C.char) string
(来自https://golang.org/cmd/cgo/)
例如:
str := C.GoString(a)
fmt.Println(str)
段错误
我对Python模块开发不太熟悉,但我可以假设,故障发生的原因是因为Python方法期望返回一个有效的PyObject*
或NULL
。但你的代码没有做到这一点。startVM
的返回值没有设置,并且它不会默认为nil,Python将这个非nil指针视为有效对象并对其进行解引用,导致段错误。
指定startVM
的返回类型可能会有所帮助:
//export startVM
func startVM(self, args *C.PyObject) *C.PyObject {
// ...一些代码...
return nil
}
英文:
Nil output
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
}
Correct printing
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)
Segmentation fault.
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论