【问题标题】:Crash calling C++ virtual member from Python extension从 Python 扩展调用 C++ 虚拟成员崩溃
【发布时间】:2012-08-02 11:43:52
【问题描述】:

我正在尝试围绕一些 C++ 类编写一个瘦包装器,以便从 Python 调用它们。具体问题是,如果virtual 在下面未注释,那么尝试创建Foo() 会使解释器崩溃。我没有兴趣将此代码重写为 Boost::python、SWIG 或 Pyxxx 来解决这个问题 - 这是从一个更大的系统中提取的我想知道它为什么会发生,尽管如果这些库中的任何一个解决了这个问题我很想知道他们是如何实现它的。如果我在系统中设置了扩展模块并注册了类型,那么来自 Python 解释器的调用将毫无问题地工作,无论是使用 extern "C" 还是在类中使用静态成员来注册 Python 类型的插槽。

如果我声明一个成员是虚拟的,那么尝试调用它(在 Python 解释器的调用中)会导致内存访问错误的崩溃。如果我将类成员打印为指针,访问的地址就是我得到的偏移量。我有特定的代码要遵循,但基本问题是:Python 调用的 C 运行时环境中的某些东西是否会破坏虚拟类成员函数的发送? Python 是 v2.6.7,C++ 扩展是由 GCC 4.2.1 编译的。有相关问题提示Boost::python支持这个,是直接做还是通过类成员函数模拟?

// PythonType is a class that inherits from PyTypeObject and fills in defaults ...

extern "C" int initHook(PyObject *self, PyObject *args, PyObject *kwds);
class Foo
{
public:
static PythonType thePyType;
  static void registerType(PyObject *module)
  {
    thePyType.tp_init = initHook;
    PyType_Ready(&thePyType);
    PyModule_AddObject(module, thePyType.tp_name, (PyObject*)&thePyType);
  }

  /*virtual*/ void insert()
  {
    printf("Inserted\n");
  }
};
PythonType Foo::thePyType("Foo",sizeof(Foo));
extern "C" {
  int initHook(PyObject *self, PyObject *args, PyObject *kwds)
  {
    ((Foo*)self)->insert();
  }
}

【问题讨论】:

  • 我不知道 python 绑定是如何工作的,但你绝对不能像这样强制转换 C++ 对象。

标签: c++ python python-extensions


【解决方案1】:

感谢 Let_Me_Be,您的评论让我走上了正轨。问题是 Foo 对象的内存正在被 Python 解释器分配。这并没有在我的其他代码中造成问题,因为它不依赖于正在初始化的 vtable。最简单的解决方案是使用placement-new 来确保实际调用ctor,然后虚拟成员正常工作:

 int initHook(PyObject *self, PyObject *args, PyObject *kwds)
 {
    new(self) Foo();
    ((Foo*)self)->insert();
 }

On VTable pointers and malloc有更详细的问答。

【讨论】:

  • 您在 PyObject* 上使用放置 new 并在该内存位置初始化另一个完全不相关的类型? CPython 应该如何管理它?
  • PyTypeObject 携带未知信息,size 字段是为了可以为这个未知结构分配足够的空间。 PyObject* 实际上并不是针对 PyObject,而是针对这个已分配然后作为这种类型传递的未知结构。然后 Python 扩展代码将指针转换为真正的类型以使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-04
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多