【问题标题】:Error in writing Python functions in Crystal-lang在 Crystal-lang 中编写 Python 函数时出错
【发布时间】:2018-07-20 17:57:25
【问题描述】:

我正在尝试通过 C Python API 用水晶语言编写一些 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-API 周围的 Crystal 绑定,但 PyCFunction_NewEx 采用来自 PyCFunction_New 的两个参数(方法对象和一个 self)加上一个 PyObject *module。所以,我不知道为什么这甚至可以编译,因为您将两个参数传递给三个参数的 C 函数。如果它编译,我想第三个参数很有可能最终全为 0,所以它会被读取为空模块,这会意外地起作用。但是将 int 值 1 作为 self 参数传递似乎并不正确。
  • 或者Crystal可能会以某种方式在某处为您添加self......在这种情况下,您将int 1作为模块传递。显然它不是一个模块对象,如果解释器只是假设它没有检查,我可以很容易地看到段错误。
  • 你能解释一下(a)这个函数应该做什么,以及(b)你试图将PyCFunction_New的哪个参数传递给number,以及为什么你认为这是有道理的,弄清楚如何解决它可能会更容易。
  • @abarnert 这段代码只是测试这种东西是否有效。我正在创建一种嵌入式语言,我想将一些函数从这种语言传递给 python。唯一的方法是通过 PyCFuncObject 将 Crystal proc 传递给 python。我还必须将一些数据传递给这个 proc 来执行我的语言的方法,这就是为什么我试图将 int 设置为 self.但是上面的代码有效(所以你应该打印 1),当 Python 尝试释放 PyCFuncObject 的内存时,问题就出现了。
  • 这是我不明白的:导致程序崩溃的无效指针在哪里?而且我很确定是在我设置自我时...

标签: python python-c-api crystal-lang


【解决方案1】:

这里的根本问题是您正在调用一个只有两个参数的三个参数的 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.5in 2.7,以及自从该功能被添加到 API in 2.3 以来的所有其他版本。 NewEx 的全部意义在于允许您传递一个模块。

据推测,该函数期望第三个参数在寄存器或堆栈中,而您没有在其中放置任何内容,因此您传递的内容完全是任意的。稍微不同的代码会在这些地方留下完全不同的值,因此得到不同的结果也就不足为奇了:

  • 如果该值恰好为 0,那很好;您可以将NULL 作为module 值传递。
  • 如果值恰好是指向未映射内存的东西,例如 1(如在原始 C long/long long 中,而不是 PyLongObject),您应该从尝试 @987654333 中得到一个段错误@模块。
  • 如果该值恰好是指向内存中某个随机事物的指针,则 incref 将起作用,但会破坏该随机事物。这几乎可以做任何事情,但是在以后的某个任意时间点出现神秘的段错误几乎是它可以做的最不令人惊讶的事情。

同时,来自评论:

我之所以调用 PyCFunction_NewEx,是因为 PyCFunction_New 是源代码中的一个宏。

如果您使用的是 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

【讨论】:

  • Crystal 编译器不会引发任何错误,因为它不知道 python 库的 API。这就是 lib 定义的用途,它本质上与 C 头文件相同。如果 C 中的标头定义不正确,则调用缺少参数的函数,C 编译器也不会抱怨。
  • @JohannesMüller 所以它在任何时候都不会解析 C 头文件来检查?好的,知道了。
  • 不。一切尽在 Crystal 中。
  • @abarnert 添加第三个参数到 PyCFunction_NewEx 代码停止崩溃。这意味着问题是由这些东西引起的:module 参数是一个垃圾指针,会导致段错误
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多