我正在尝试通过 C Python API 在 crystal-lang 中编写一些 python 函数。
我的代码如下:
METH_VARARGS = 0x0001
@[Link("python3.5m")]
lib Python
alias PyObject = Void*
struct PyMethodDef
name : UInt8*
func : Void*
flags : LibC::Int
doc : UInt8*
end
fun Py_Initialize
fun Py_Finalize
fun PyObject_CallObject(func : PyObject, args : PyObject) : PyObject
fun PyCFunction_NewEx(method : PyMethodDef*, __self__ : PyObject, ) : PyObject
fun PyLong_AsLong(n : PyObject) : Int64
fun PyLong_FromLong(n : Int64) : PyObject
end
def new_method_def(name : String, function, flags : LibC::Int)
x = Pointer(Python::PyMethodDef).malloc(1)
x.value.name = name.to_unsafe
x.value.func = function
x.value.flags = flags
x.value.doc = nil
x
end
Python.Py_Initialize
a = ->(args : Void*) {
puts Python.PyLong_AsLong(args)
Pointer(Void).null
}
name = "num"
number = Python.PyLong_FromLong(1)
Python.Py_IncRef(number)
method = Python.PyCFunction_NewEx(new_method_def(name,a.pointer,METH_VARARGS),number)
Python.PyObject_CallObject(method,Pointer(Void).null)
Python.Py_Finalize
如果我在 PyCFunction_NewEx
中设置 nil
而不是 number
一切正常,但正如代码所示,它会抛出无效访问内存异常当调用 Py_Finalize
时。
我不明白是什么原因造成的。
有人能帮我吗?
最佳答案
这里的根本问题是您调用了一个只有两个参数的三个参数的 C 函数。
很遗憾,PyCFunction_NewEx
is missing from the documentation, despite being a public API function.但是所有使用它的例子都传递了三个参数。如果你去the source :
PyObject *
PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
那是 3.7,但这是一样的 in 3.5和 in 2.7 ,以及自该函数添加到 API 以来的所有其他版本 in 2.3 . NewEx
的全部意义在于允许您传递一个模块。
大概,该函数期望第三个参数在寄存器或堆栈中,而您没有在其中放置任何东西,因此您传递的内容完全是任意的。稍微不同的代码会在这些地方留下完全不同的值,因此您得到不同的结果也就不足为奇了:
- 如果该值恰好为 0,那很好;您可以将
NULL
作为module
值传递。 - 如果该值恰好指向未映射的内存,例如
1
(如在原始 C long/long long 中,而不是 PyLongObject),您应该从incref
模块的尝试。 - 如果该值恰好是指向内存中某个随机事物的指针,则 incref 将起作用,但会破坏该随机事物。它几乎可以做任何事情,但在以后的某个任意时间点出现神秘的段错误几乎是它可以做的最不令人惊讶的事情。
同时,来自评论:
I am calling PyCFunction_NewEx because PyCFunction_New is a marco in the source code.
如果您使用的是 Python 2.3-2.6 或 3.0-3.2,那么没问题。但是在以后的版本中,包括你说你正在使用的 3.5,CPython 特意将 PyCFunction_New
定义为一个函数,这样它就会出现在 API 中(甚至是稳定的 API ,对于 3.x)。参见 3.5例如:
/* undefine macro trampoline to PyCFunction_NewEx */
#undef PyCFunction_New
PyAPI_FUNC(PyObject *)
PyCFunction_New(PyMethodDef *ml, PyObject *self)
{
return PyCFunction_NewEx(ml, self, NULL);
}
所以,您真的可以调用 PyCFunction_New
。
关于python - 在 Crystal-lang 中编写 Python 函数时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51448030/