关于C++与Python扩展的困惑:如何获取C++值对应的Python值

1 投票
2 回答
585 浏览
提问于 2025-04-15 22:13

我想把一些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 个回答

2

我可以在这里给个真诚的建议吗?当你尝试一些新东西时,不要一下子给自己扔一百行代码。首先,试试最简单的测试案例。比如,先让一个C++扩展程序工作,它只返回一个值123。(选择0或1可能会意外成功,所以我选了一个容易识别的值,避免出现意外。)然后再让这个扩展程序把你传入的数字翻倍,再然后加两个数字,等等。

慢慢接近问题!:-)

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()的返回值检查。

撰写回答