关于C++与Python扩展的困惑:如何获取C++值对应的Python值
我想把一些Python代码转换成C++,这样可以提高速度,但这并不是简单地创建一个C++函数然后调用几次那么简单。我不知道怎么把Python中的整数对象转换成C++中的整数。我有一个整数,它是一个对象的属性,我想用这个整数。另外,我还有一些整数,它们在这个对象的一个列表里,我也需要用到。
我想测试用这个函数来创建一个C++扩展:
def setup_framebuffer(surface,flip=False):
#Create texture if not done already
if surface.texture is None:
create_texture(surface)
#Render child to parent
if surface.frame_buffer is None:
surface.frame_buffer = glGenFramebuffersEXT(1)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, c_uint(int(surface.frame_buffer)))
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, surface.texture, 0)
glPushAttrib(GL_VIEWPORT_BIT)
glViewport(0,0,surface._scale[0],surface._scale[1])
glMatrixMode(GL_PROJECTION)
glLoadIdentity() #Load the projection matrix
if flip:
gluOrtho2D(0,surface._scale[0],surface._scale[1],0)
else:
gluOrtho2D(0,surface._scale[0],0,surface._scale[1])
这个函数会调用create_texture,所以我需要把这个函数传递给C++函数,我会通过第三个参数来实现这一点。这是我到目前为止的进展,我在尽量按照Python文档上的信息来做:
#include <Python.h>
#include <GL/gl.h>
static PyMethodDef SpamMethods[] = {
...
{"setup_framebuffer", setup_framebuffer, METH_VARARGS,"Loads a texture from a Surface object to the OpenGL framebuffer."},
...
{NULL, NULL, 0, NULL} /* Sentinel */
};
static PyObject * setup_framebuffer(PyObject *self, PyObject *args){
bool flip;
PyObject *create_texture, *arg_list,*pyflip,*frame_buffer_id;
if (!PyArg_ParseTuple(args, "OOO", &surface,&pyflip,&create_texture)){
return NULL;
}
if (PyObject_IsTrue(pyflip) == 1){
flip = true;
}else{
flip = false;
}
Py_XINCREF(create_texture);
//Create texture if not done already
if(texture == NULL){
arglist = Py_BuildValue("(O)", surface)
result = PyEval_CallObject(create_texture, arglist);
Py_DECREF(arglist);
if (result == NULL){
return NULL;
}
Py_DECREF(result);
}
Py_XDECREF(create_texture);
//Render child to parent
frame_buffer_id = PyObject_GetAttr(surface, Py_BuildValue("s","frame_buffer"))
if(surface.frame_buffer == NULL){
glGenFramebuffersEXT(1,frame_buffer_id);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, surface.frame_buffer));
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, surface.texture, 0);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,surface._scale[0],surface._scale[1]);
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //Load the projection matrix
if (flip){
gluOrtho2D(0,surface._scale[0],surface._scale[1],0);
}else{
gluOrtho2D(0,surface._scale[0],0,surface._scale[1]);
}
Py_INCREF(Py_None);
return Py_None;
}
PyMODINIT_FUNC initcscalelib(void){
PyObject *module;
module = Py_InitModule("cscalelib", Methods);
if (m == NULL){
return;
}
}
int main(int argc, char *argv[]){
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(argv[0]);
/* Initialize the Python interpreter. Required. */
Py_Initialize();
/* Add a static module */
initscalelib();
}
2 个回答
我可以在这里给个真诚的建议吗?当你尝试一些新东西时,不要一下子给自己扔一百行代码。首先,试试最简单的测试案例。比如,先让一个C++扩展程序工作,它只返回一个值123。(选择0或1可能会意外成功,所以我选了一个容易识别的值,避免出现意外。)然后再让这个扩展程序把你传入的数字翻倍,再然后加两个数字,等等。
慢慢接近问题!:-)
要把Python中的整数转成C语言的int
,可以用PyInt_AsLong()
这个函数,然后再进行类型转换(不过你可能更想用C语言的long类型)。如果要把C的整数转回Python,就用PyInt_FromLong()
。不过我不太清楚你代码中具体在哪个地方需要这样做,不过我对你的代码有几点其他的建议:
从概念上讲,
PyObject_IsTrue()
这个函数返回的是一个布尔值。不要把它和1比较,直接把它当作布尔值使用就可以了。不过,你应该检查一下它是否返回了错误,错误的返回值是-1。通常的检查方式是看返回值是否小于0。如果你不检查错误返回值,就会忽略异常,但异常对象会留在那儿,这样是不好的。对
create_texture
使用Py_XINCREF()
其实是没必要的。你对args
这个元组有一个借用的引用,而这个元组又持有create_texture
对象的引用。在你的函数返回之前,create_texture
是不会消失的。只有在你需要在这个函数调用之后再使用它时,才需要增加引用计数。如果真的需要增加引用计数,你也不需要用Py_XINCREF()
,因为它不会是NULL。如果你确实需要增加引用计数,还需要记得在错误返回的情况下减少引用计数。与其为了调用
PyEval_CallObject()
而创建一个参数元组,不如直接调用PyObject_CallFunctionObjectArgs(create_texture, arglist, NULL)
或者PyObject_CallFunction(create_texture, "O", arglist)
。Py_BuildValue("s", "frame_buffer")
并不是获取"frame_buffer"
这个Python字符串的正确方法。更好的方法是用PyString_FromString()
。不过这两种方法都会返回新的引用,而PyObject_GetAttr()
不会消耗属性名称的引用,这样就会导致引用泄漏。你应该都不要用这些,而是使用PyObject_GetAttrString()
,它可以直接接受属性名称作为const char*
。记得检查所有可能返回错误值的函数的返回值(几乎所有的Python API函数都是这样)。除了
PyTrue_IsTrue()
,你也忘记了Py_BuildValue()
和PyObject_GetAttr()
的返回值检查。