我是靠谱客的博主 忧伤镜子,最近开发中收集的这篇文章主要介绍python c++混合编程文档缩减版笔记 - 1,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

官方文档

A Simple Example

  • The C extension interface is specific to CPython, and extension modules do not work on other Python implementations.

    • 使用ctypes或者cffi有更好的可移植性
  • An important convention throughout the Python interpreter is the following: when a function fails, it should set an exception condition and return an error value (usually a NULL pointer). Exceptions are stored in a static global variable inside the interpreter; if this variable is NULL no exception has occurred. A second global variable stores the “associated value” of the exception (the second argument to raise). A third variable contains the stack traceback in case the error originated in Python code. These three variables are the C equivalents of the result in Python of sys.exc_info() (see the section on module sys in the Python Library Reference). It is important to know about them to understand how errors are passed around.

  • 几个常见的错误函数

    • PyErr_SetString()
    • PyErr_SetFromErrno()
    • PyErr_SetObject()
    • PyErr_Occurred()
    • PyErr_Clear()
    • You don’t need to Py_INCREF() the objects passed to any of these functions.
  • malloc 之类的函数内存分配失败时必须调用 PyErr_NoMemory(),所有的对象创建函数比如PyLong_FromLong()等是这样实现的

  • 除掉一些 PyArg_ParseTuple() 之类的函数,函数返回值是>=0成功,<0失败,和unix保持一致

  • be careful to clean up garbage (by making Py_XDECREF() or Py_DECREF() calls for objects you have already created) when you return an error indicator!

  • 错误类型不要瞎报

  • 可以自定义模块异常

  • When a function f that calls another function g detects that the latter fails, f should itself return an error value (usually NULL or -1). It should not call one of the PyErr_() functions — one has already been called by g. f’s caller is then supposed to also return an error indication to its caller, again without calling PyErr_(), and so on — the most detailed cause of the error was already reported by the function that first detected it.

    • 这也是PyArg_ParseTuple()失败直接返回NULL的原因,PyArg_ParseTuple()会自己触发异常
    • 可以发现 return NULL 和 PyErr 哥俩好
  • If you have a C function that returns no useful argument (a function returning void), the corresponding Python function must return None. You need this idiom to do so (which is implemented by the Py_RETURN_NONE macro):

    Py_INCREF(Py_None);
    return Py_None;
    
  • Note the third entry (METH_VARARGS). This is a flag telling the interpreter the calling convention to be used for the C function. It should normally always be METH_VARARGS or METH_VARARGS | METH_KEYWORDS;

  • The METH_KEYWORDS 需要用PyArg_ParseTupleAndKeywords()

  • When the Python program imports module spam for the first time, PyInit_spam() is called.

  • When embedding Python, the PyInit_spam() function is not called automatically unless there’s an entry in the PyImport_Inittab table. To add the module to the initialization table, use PyImport_AppendInittab().


Calling Python Functions from C

  • This function must be registered with the interpreter using the METH_VARARGS flag

  • The macros Py_XINCREF() and Py_XDECREF() increment/decrement the reference count of an object and are safe in the presence of NULL pointers

  • Later, when it is time to call the function, you call the C function PyObject_CallObject(). This function has two arguments, both pointers to arbitrary Python objects: the Python function, and the argument list. The argument list must always be a tuple object, whose length is the number of arguments. To call the Python function with no arguments, pass in NULL, or an empty tuple; to call it with one argument, pass a singleton tuple. Py_BuildValue() returns a tuple when its format string consists of zero or more format codes between parentheses.

  • The return value of PyObject_CallObject() is “new”: either it is a brand new object, or it is an existing object whose reference count has been incremented. So, unless you want to save it in a global variable, you should somehow Py_DECREF() the result, even (especially!) if you are not interested in its value.

    • PyObject_CallObject()的返回值如果你不需要的话应该Py_DECREF(),但是需要对返回值做NULL判定,所以可以用Py_XDECREF()

Extracting Parameters in Extension Functions

  • PyArg_ParseTuple()的参数类型必须对上,对不上可能导致崩溃或者其他未定义行为

  • Note that any Python object references which are provided to the caller are borrowed references; do not decrement their reference count!

  • int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, const char *format, char *kwlist[], …);

  • kwlist参数是以NULL结尾的字符串列表

  • PyArg_ParseTupleAndKeywords()使用关键字参数时无法解析嵌套的元组!传入的关键字参数不在kwlist中,这将导致引发TypeError。

  • Py_BuildValue的参数只能是值不能是指针


Reference Counts

  • Py_DECREF() also frees the object when the count reaches zero. For flexibility, it doesn’t call free() directly — rather, it makes a call through a function pointer in the object’s type object. For this purpose (and others), every object also contains a pointer to its type object.
  • Nobody “owns” an object; however, you can own a reference to an object.
  • The owner of a reference is responsible for calling Py_DECREF() when the reference is no longer needed. Ownership of a reference can be transferred. There are three ways to dispose of an owned reference: pass it on, store it, or call Py_DECREF(). Forgetting to dispose of an owned reference creates a memory leak.
  • It is also possible to borrow [2] a reference to an object. The borrower of a reference should not call Py_DECREF(). The borrower must not hold on to the object longer than the owner from which it was borrowed. Using a borrowed reference after the owner has disposed of it risks using freed memory and should be avoided completely [3].
  • The advantage of borrowing over owning a reference is that you don’t need to take care of disposing of the reference on all possible paths through the code — in other words, with a borrowed reference you don’t run the risk of leaking when a premature exit is taken. The disadvantage of borrowing over owning is that there are some subtle situations where in seemingly correct code a borrowed reference can be used after the owner from which it was borrowed has in fact disposed of it.
  • A borrowed reference can be changed into an owned reference by calling Py_INCREF(). This does not affect the status of the owner from which the reference was borrowed — it creates a new owned reference, and gives full owner responsibilities (the new owner must dispose of the reference properly, as well as the previous owner).

Ownership Rules

  • Whenever an object reference is passed into or out of a function, it is part of the function’s interface specification whether ownership is transferred with the reference or not.
  • Most functions that return a reference to an object pass on ownership with the reference. In particular, all functions whose function it is to create a new object, such as PyLong_FromLong() and Py_BuildValue(), pass ownership to the receiver.
  • Many functions that extract objects from other objects also transfer ownership with the reference, for instance PyObject_GetAttrString().
    • PyTuple_GetItem(), PyList_GetItem(), PyDict_GetItem(), and PyDict_GetItemString() all return references that you borrow from the tuple, list or dictionary.
  • The function PyImport_AddModule() also returns a borrowed reference, even though it may actually create the object it returns: this is possible because an owned reference to the object is stored in sys.modules.
  • When you pass an object reference into another function, in general, the function borrows the reference from you — if it needs to store it, it will use Py_INCREF() to become an independent owner.
    • There are exactly two important exceptions to this rule: PyTuple_SetItem() and PyList_SetItem(). These functions take over ownership of the item passed to them — even if they fail! (Note that PyDict_SetItem() and friends don’t take over ownership — they are “normal.”)
  • When a C function is called from Python, it borrows references to its arguments from the caller. The caller owns a reference to the object, so the borrowed reference’s lifetime is guaranteed until the function returns. Only when such a borrowed reference must be stored or passed on, it must be turned into an owned reference by calling Py_INCREF().
  • The object reference returned from a C function that is called from Python must be an owned reference — ownership is transferred from the function to its caller.
  • 陷阱
    void bug(PyObject *list)
    {
    PyObject *item = PyList_GetItem(list, 0);
    PyList_SetItem(list, 1, PyLong_FromLong(0L));
    PyObject_Print(item, stdout, 0); /* BUG! */
    }
    
    看起来 item 是借用于 list 中的最后 print 没有 bug,但是这里隐藏的问题是 SetItem 会转移引用,如果旧的 list[1] 刚好没有其他引用被释放了,会执行list[1].__del__的方法,那这个时候就尴尬了,你不知道这个__del__会做什么万一把item给释放了那可就有意思了。正常的做法如下。
    void bug(PyObject *list)
    {
    PyObject *item = PyList_GetItem(list, 0);
    Py_INCREF(item);
    PyList_SetItem(list, 1, PyLong_FromLong(0L));
    PyObject_Print(item, stdout, 0);
    Py_DECREF(item);
    }
    

NULL Pointers

  • 时刻注意NULLpointer的检查,除了 Py_XINCREF() and Py_XDECREF() 别的都不会检查,要从源头source检查NULL

Providing a C API for an Extension Module

  • 这部分我不怎么用,后面有需要了再看

#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *SpamError;
static PyObject *spam_system(PyObject *self, PyObject *args) {
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command)) return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}
static PyMethodDef SpamMethods[] = {
{"system", spam_system, METH_VARARGS, "Execute a shell command."},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static struct PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT, "spam", /* name of module */
"spam_doc",
/* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
SpamMethods};
PyMODINIT_FUNC PyInit_spam(void) {
PyObject *m;
m = PyModule_Create(&spammodule);
if (m == NULL) return NULL;
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_XINCREF(SpamError);
if (PyModule_AddObject(m, "error", SpamError) < 0) {
Py_XDECREF(SpamError);
Py_CLEAR(SpamError);
Py_DECREF(m);
return NULL;
}
return m;
}
int main1(int argc, char *argv[]) {
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
fprintf(stderr, "Fatal error: cannot decode argv[0]n");
exit(1);
}
/* Add a built-in module, before Py_Initialize */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules tablen");
exit(1);
}
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);
/* Initialize the Python interpreter.
Required.
If this step fails, it will be a fatal error. */
Py_Initialize();
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
auto pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'n");
}
PyMem_RawFree(program);
return 0;
}
int main(int argc, char *argv[]) {
main1(argc, argv);
// main2(argc, argv);
}
//
static PyObject *keywdarg_parrot(PyObject *self, PyObject *args,
PyObject *keywds) {
int voltage;
const char *state = "a stiff";
const char *action = "voom";
const char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist, &voltage,
&state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.n", action,
voltage);
printf("-- Lovely plumage, the %s -- It's %s!n", type, state);
Py_RETURN_NONE;
}
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)(void (*)(void))keywdarg_parrot,
METH_VARARGS | METH_KEYWORDS, "Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
static struct PyModuleDef keywdargmodule = {PyModuleDef_HEAD_INIT, "keywdarg",
NULL, -1, keywdarg_methods};
PyMODINIT_FUNC PyInit_keywdarg(void) {
return PyModule_Create(&keywdargmodule);
}
//
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *my_callback = NULL;
static PyObject *my_set_callback(PyObject *dummy, PyObject *args) {
PyObject *result = NULL;
PyObject *temp;
if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp);
/* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp;
/* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
static PyObject *PyObject_Call_CallObject_F() {
int arg;
PyObject *arglist;
PyObject *dict;
PyObject *result;
arg = 123;
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
dict = Py_BuildValue("{s:i}", "name", arg);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(arglist);
if (result == NULL) return NULL; /* Pass error back */
// use result
Py_DECREF(result);
}

最后

以上就是忧伤镜子为你收集整理的python c++混合编程文档缩减版笔记 - 1的全部内容,希望文章能够帮你解决python c++混合编程文档缩减版笔记 - 1所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(70)

评论列表共有 0 条评论

立即
投稿
返回
顶部