用Golang和C编写的Python模块

Dar*_*ull 1 c python go cgo

我按照本教程

用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);
}
Run Code Online (Sandbox Code Playgroud)

和此代码在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() {}  
Run Code Online (Sandbox Code Playgroud)

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

bge*_*668 5

无输出

该功能:

int PyArg_ParseTuple_S(PyObject * args, char* a) {
    return PyArg_ParseTuple(args, "s", &a);
}
Run Code Online (Sandbox Code Playgroud)

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

var a *C.char
C.PyArg_ParseTuple_S(args, a)
// Here `a` is not set, so it keeps its default value: nil.
Run Code Online (Sandbox Code Playgroud)

您可以通过将指针传递给字符串而不是字符串本身来解决此问题:

// 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
}
Run Code Online (Sandbox Code Playgroud)

正确打印

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

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

// C string to Go string
func C.GoString(*C.char) string
Run Code Online (Sandbox Code Playgroud)

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

例如:

str := C.GoString(a)
fmt.Println(str)
Run Code Online (Sandbox Code Playgroud)

分段故障。

我对python模块开发不熟悉,但是我可以假设发生了这种错误,因为python方法应该返回有效的PyObject*NULL。但是您的代码没有执行任何操作。的返回值startVM未设置,并且默认不为nil,python将此非nil指针作为有效对象接受并取消引用,这会导致分段错误。

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

//export startVM
func startVM(self, args *C.PyObject) *C.PyObject {  
    // ...some code...
    return nil
}
Run Code Online (Sandbox Code Playgroud)