我正在尝试通过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
时,它会抛出一个无效的访问内存异常。我不明白是什么引起的。有人能帮我吗?
发布于 2018-07-21 05:24:07
这里的根本问题是,您调用的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,并且在函数被添加到in 2.3接口之后的所有其他版本中都是如此。NewEx
的全部意义在于允许您传递一个模块。
假设函数需要第三个参数,或者在寄存器中,或者在堆栈中,而您没有将任何内容放在那里,所以传递的内容完全是任意的。稍微不同的代码将在这些地方留下完全不同的值,所以得到不同的结果也就不足为奇了:
NULL
作为代码值传递。module
1
(就像在原始C incref
/ long long中那样,而不是1
),那么您应该从尝试incref
模块时获得段错误。<1
>h116如果此值恰好是指向内存中某个随机对象的指针,则增量将起作用,但会破坏该随机对象。这几乎可以做任何事情,但在稍后的某个任意时刻出现神秘的段错误几乎是它所能做的最不令人惊讶的事情。
与此同时,来自一条评论:
我之所以调用PyCFunction_NewEx,是因为PyCFunction_New在源代码中是一个
。
如果您使用的是Python 2.3-2.6或3.0-3.2,那么当然可以。但是在以后的版本中,包括你说你正在使用的3.5版本,CPython会特意将PyCFunction_New
定义为一个函数,这样它就会出现在API中(甚至是3.x的稳定API )。例如,请参阅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
。
https://stackoverflow.com/questions/51448030
复制相似问题