【问题标题】:Python C-API Object AllocationPython C-API 对象分配
【发布时间】:2010-10-09 01:35:10
【问题描述】:

我想使用 new 和 delete 运算符来创建和销毁我的对象。

问题是python似乎把它分成了几个阶段。 tp_new、tp_init 和 tp_alloc 用于创建,tp_del、tp_free 和 tp_dealloc 用于销毁。但是 c++ 只有 new 分配和完全构造对象,而 delete 则破坏和释放对象。

我需要提供哪些 python tp_* 方法以及它们必须做什么?

我还希望能够直接在 c++ 中创建对象,例如“PyObject *obj = new MyExtensionObject(args);”我是否还需要以某种方式重载 new 运算符来支持这一点?

我也希望能够在 python 中对我的扩展类型进行子类化,我需要做些什么来支持这一点吗?

我正在使用 python 3.0.1。

编辑: 好的,tp_init 似乎使对象对于我正在做的事情有点太可变了(例如,获取一个纹理对象,在创建后更改内容很好,但是更改它的基本方面,例如大小、bitdept 等会破坏很多假设这些事情是固定的现有c ++东西)。如果我不实现它,它只会阻止人们在构造之后调用 __init__ (或者至少忽略调用,就像元组一样)。或者,如果 tp_init 在同一个对象上被多次调用,我应该有一些引发异常的标志吗?

除此之外,我认为其余的大部分都已排序。

extern "C"
{
    //creation + destruction
    PyObject* global_alloc(PyTypeObject *type, Py_ssize_t items)
    {
        return (PyObject*)new char[type->tp_basicsize + items*type->tp_itemsize];
    }
    void global_free(void *mem)
    {
        delete[] (char*)mem;
    }
}
template<class T> class ExtensionType
{
    PyTypeObject *t;
    ExtensionType()
    {
        t = new PyTypeObject();//not sure on this one, what is the "correct" way to create an empty type object
        memset((void*)t, 0, sizeof(PyTypeObject));
        static PyVarObject init = {PyObject_HEAD_INIT, 0};
        *((PyObject*)t) = init;

        t->tp_basicsize = sizeof(T);
        t->tp_itemsize  = 0;

        t->tp_name = "unknown";

        t->tp_alloc   = (allocfunc) global_alloc;
        t->tp_free    = (freefunc)  global_free;
        t->tp_new     = (newfunc)   T::obj_new;
        t->tp_dealloc = (destructor)T::obj_dealloc;
        ...
    }
    ...bunch of methods for changing stuff...
    PyObject *Finalise()
    {
    ...
    }
};
template <class T> PyObjectExtension : public PyObject
{
...
    extern "C" static PyObject* obj_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
    {
        void *mem = (void*)subtype->tp_alloc(subtype, 0);
        return (PyObject*)new(mem) T(args, kwds)
    }
    extern "C" static void obj_dealloc(PyObject *obj)
    {
        ~T();
        obj->ob_type->tp_free(obj);//most of the time this is global_free(obj)
    }
...
};
class MyObject : PyObjectExtension<MyObject>
{
public:
    static PyObject* InitType()
    {
        ExtensionType<MyObject> extType();
        ...sets other stuff...
        return extType.Finalise();
    }
    ...
};

【问题讨论】:

    标签: c++ python c python-3.x python-c-api


    【解决方案1】:

    这些文档位于http://docs.python.org/3.0/c-api/typeobj.htmlhttp://docs.python.org/3.0/extending/newtypes.html 描述了如何制作自己的字体。

    tp_alloc 为实例分配低级内存。这相当于 malloc(),加上将 refcnt 初始化为 1。Python 有它自己的分配器 PyType_GenericAlloc,但是一个类型可以实现一个专门的分配器。

    tp_new 与 Python 的 __new__ 相同。与指向数据的指针相比,它通常用于数据存储在实例本身中的不可变对象。例如,字符串和元组将它们的数据存储在实例中,而不是使用 char * 或 PyTuple *。

    对于这种情况,tp_new 根据输入参数计算出需要多少内存,并调用 tp_alloc 来获取内存,然后初始化基本字段。 tp_new 不需要调用 tp_alloc。例如,它可以返回一个缓存的对象。

    tp_init 与 Python 的 __init__ 相同。你的大部分初始化都应该在这个函数中。

    __new__ 和 __init__ 的区别称为two-stage initialization,或two-phase initialization

    你说“c++ just has new”但这是不正确的。 tp_alloc 对应 C++ 中的自定义 arena 分配器,__new__ 对应自定义类型分配器(工厂函数),__init__ 更像是构造函数。最后一个链接更多地讨论了 C++ 和 Python 风格之间的相似之处。

    另请阅读http://www.python.org/download/releases/2.2/descrintro/,了解有关 __new__ 和 __init__ 如何交互的详细信息。

    您写道您想要“直接在 C++ 中创建对象”。这相当困难,因为至少您必须将在对象实例化期间发生的任何 Python 异常转换为 C++ 异常。您可以尝试查看 Boost::Python 以获得有关此任务的一些帮助。或者您可以使用两阶段初始化。 ;)

    【讨论】:

      【解决方案2】:

      我根本不知道python的API,但是如果python把分配和初始化分开了,你应该可以使用placement new。

      例如:

       // tp_alloc
       void *buffer = new char[sizeof(MyExtensionObject)];
       // tp_init or tp_new (not sure what the distinction is there)
       new (buffer) MyExtensionObject(args);
       return static_cast<MyExtensionObject*>(buffer);
      
       ...
       // tp_del
       myExtensionObject->~MyExtensionObject(); // call dtor
       // tp_dealloc (or tp_free? again I don't know the python apis)
       delete [] (static_cast<char*>(static_cast<void*>(myExtensionObject)));
      

      【讨论】:

      • Placement new 会很直观,但遗憾的是 new (p) Class(args); 在调用构造函数之前执行零初始化,这会清除 python 分配/初始化代码已放入对象内存的值(例如引用数数)。另外,使用 new char[n] 然后覆盖结构是危险的,因为保证的内存对齐只有 1。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-30
      • 1970-01-01
      相关资源
      最近更新 更多