python - 在 Crystal-lang 中编写 Python 函数时出错

标签 python python-c-api crystal-lang

我正在尝试通过 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.5in 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/

相关文章:

Python - ThreadPoolExecutor 阻塞。如何解锁

python - append 到列表列表

python-3.x - pip 如何告诉 Python 如何导入 C 扩展

crystal-lang - 我无法在 Alpine 边缘下构建 Crystal 程序的静态二进制文件

python明确不传递可选参数

python - 霍夫曼编码问题

python - 将新的 PyObject * 从 C++ 返回到 Python 最终会出现段错误

Python C API,新对象的高引用计数

arm - 如何在树莓派上安装 crystal-lang?

deployment - 如何在 Ubuntu 上部署 Amber 应用程序?