Python模块是用Golang和C编写的。

huangapple go评论200阅读模式
英文:

Python module written in Golang and C

问题

我按照这个教程编写了以下代码,使用C语言:

  1. #define Py_LIMITED_API
  2. #include <Python.h>
  3. PyObject * startVM(PyObject *, PyObject *);
  4. int PyArg_ParseTuple_S(PyObject * args, char* a) {
  5. return PyArg_ParseTuple(args, "s", &a);
  6. }
  7. static PyMethodDef FooMethods[] = {
  8. {"startVM", startVM, METH_VARARGS, "Starts."},
  9. {NULL, NULL, 0, NULL}
  10. };
  11. static struct PyModuleDef foomodule = {
  12. PyModuleDef_HEAD_INIT, "foo", NULL, -1, FooMethods
  13. };
  14. PyMODINIT_FUNC PyInit_foo(void) {
  15. return PyModule_Create(&foomodule);
  16. }

以及使用Go语言编写的代码:

  1. package main
  2. import "fmt"
  3. // #cgo pkg-config: python3
  4. // #define Py_LIMITED_API
  5. // #include <Python.h>
  6. // int PyArg_ParseTuple_S(PyObject *,char *);
  7. import "C"
  8. //export startVM
  9. func startVM(self, args *C.PyObject) {
  10. var a *C.char
  11. if C.PyArg_ParseTuple_S(args, a) == 0 {
  12. //return nil
  13. }
  14. fmt.Println(a)
  15. //return C.PyBytes_FromString(&a)
  16. }
  17. func main() {}

我可以编译Go代码,但是当我在Python中使用以下命令调用模块时:python3 -c 'import foo; foo.startVM("hello")',它打印出nil并导致段错误...有人知道如何修复吗?提前感谢。

英文:

I follow this tutorial

to write this code, in C:

  1. #define Py_LIMITED_API
  2. #include &lt;Python.h&gt;
  3. PyObject * startVM(PyObject *, PyObject *);
  4. int PyArg_ParseTuple_S(PyObject * args, char* a) {
  5. return PyArg_ParseTuple(args, &quot;s&quot;, &amp;a);
  6. }
  7. static PyMethodDef FooMethods[] = {
  8. {&quot;startVM&quot;, startVM, METH_VARARGS, &quot;Starts.&quot;},
  9. {NULL, NULL, 0, NULL}
  10. };
  11. static struct PyModuleDef foomodule = {
  12. PyModuleDef_HEAD_INIT, &quot;foo&quot;, NULL, -1, FooMethods
  13. };
  14. PyMODINIT_FUNC PyInit_foo(void) {
  15. return PyModule_Create(&amp;foomodule);
  16. }

and this code in GO:

  1. package main
  2. import &quot;fmt&quot;
  3. // #cgo pkg-config: python3
  4. // #define Py_LIMITED_API
  5. // #include &lt;Python.h&gt;
  6. // int PyArg_ParseTuple_S(PyObject *,char *);
  7. import &quot;C&quot;
  8. //export startVM
  9. func startVM(self, args *C.PyObject) {
  10. var a *C.char
  11. if C.PyArg_ParseTuple_S(args, a) == 0 {
  12. //return nil
  13. }
  14. fmt.Println(a)
  15. //return C.PyBytes_FromString(&amp;a)
  16. }
  17. func main() {}

I can compile the code in go, but when I call in python the module with this command: python3 -c &#39;import foo; foo.startVM(&quot;hello&quot;)&#39;, it prints nil and results in segmentation fault...
Could someone know how to fix it?
Thanks in advance.

答案1

得分: 5

翻译结果:

空输出

这个函数:

  1. int PyArg_ParseTuple_S(PyObject * args, char* a) {
  2. return PyArg_ParseTuple(args, "s", &a);
  3. }

只会设置a本地副本,并且不会将其返回给调用函数,因为你通过值(通过复制)传递了字符串指针,所以PyArg_ParseTuple只设置了副本。

  1. var a *C.char
  2. C.PyArg_ParseTuple_S(args, a)
  3. // 这里`a`没有被设置,所以它保持默认值:nil。

你可以通过传递指向字符串的指针来解决这个问题,而不是传递字符串本身:

  1. // C
  2. int PyArg_ParseTuple_S(PyObject * args, char** a) {
  3. return PyArg_ParseTuple(args, "s", a);
  4. }
  5. // Go
  6. var a *C.char
  7. if C.PyArg_ParseTuple_S(args, &a) == 0 {
  8. // 返回nil
  9. }

正确打印

fmt.Println(a)会打印a所持有的地址,而不是它指向的字符串。Go有自己的字符串类型,不适用于C字符串。

如果你想正确打印文本,你必须使用C.GoString进行转换:

  1. // C字符串到Go字符串
  2. func C.GoString(*C.char) string

(来自https://golang.org/cmd/cgo/)

例如:

  1. str := C.GoString(a)
  2. fmt.Println(str)

段错误

我对Python模块开发不太熟悉,但我可以假设,故障发生的原因是因为Python方法期望返回一个有效的PyObject*NULL。但你的代码没有做到这一点。startVM的返回值没有设置,并且它不会默认为nil,Python将这个非nil指针视为有效对象并对其进行解引用,导致段错误。

指定startVM的返回类型可能会有所帮助:

  1. //export startVM
  2. func startVM(self, args *C.PyObject) *C.PyObject {
  3. // ...一些代码...
  4. return nil
  5. }
英文:

Nil output

This function:

  1. int PyArg_ParseTuple_S(PyObject * args, char* a) {
  2. return PyArg_ParseTuple(args, &quot;s&quot;, &amp;a);
  3. }

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.

  1. var a *C.char
  2. C.PyArg_ParseTuple_S(args, a)
  3. // 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:

  1. // C
  2. int PyArg_ParseTuple_S(PyObject * args, char** a) {
  3. return PyArg_ParseTuple(args, &quot;s&quot;, a);
  4. }
  5. // Go
  6. var a *C.char
  7. if C.PyArg_ParseTuple_S(args, &amp;a) == 0 {
  8. //return nil
  9. }

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:

  1. // C string to Go string
  2. func C.GoString(*C.char) string

(from https://golang.org/cmd/cgo/)

For example:

  1. str := C.GoString(a)
  2. 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:

  1. //export startVM
  2. func startVM(self, args *C.PyObject) *C.PyObject {
  3. // ...some code...
  4. return nil
  5. }

huangapple
  • 本文由 发表于 2016年3月21日 03:09:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/36118229.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定