Python C-API 对象分配
我想使用 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 似乎使对象对于我正在做的事情来说有点太可变了(例如,采用一个纹理对象,在创建后更改内容很好,但是更改它的基本方面,例如大小,位深度等会破坏很多现有的 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();
}
...
};
I want to use the new and delete operators for creating and destroying my objects.
The problem is python seems to break it into several stages. tp_new, tp_init and tp_alloc for creation and tp_del, tp_free and tp_dealloc for destruction. However c++ just has new which allocates and fully constructs the object and delete which destructs and deallocates the object.
Which of the python tp_* methods do I need to provide and what must they do?
Also I want to be able to create the object directly in c++ eg "PyObject *obj = new MyExtensionObject(args);" Will I also need to overload the new operator in some way to support this?
I also would like to be able to subclass my extension types in python, is there anything special I need to do to support this?
I'm using python 3.0.1.
EDIT:
ok, tp_init seems to make objects a bit too mutable for what I'm doing (eg take a Texture object, changing the contents after creation is fine, but change fundamental aspects of it such as, size, bitdept, etc will break lots of existing c++ stuff that assumes those sort of things are fixed). If I dont implement it will it simply stop people calling __init__ AFTER its constructed (or at least ignore the call, like tuple does). Or should I have some flag that throws an exception or somthing if tp_init is called more than once on the same object?
Apart from that I think ive got most of the rest sorted.
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();
}
...
};
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这些文档位于 http://docs.python.org/3.0/c -api/typeobj.html 和
http://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__ 之间的区别称为两阶段初始化,或
你说“c++ 只是有新的”,但这是不正确的。 tp_alloc 对应于 C++ 中的自定义 arena 分配器,__new__ 对应于自定义类型分配器(工厂函数),而 __init__ 更像是构造函数。 最后一个链接更多地讨论了 C++ 和 Python 风格之间的相似之处。
另请阅读 http://www.python.org/download/releases/2.2/descrintro/ 有关 __new__ 和 __init__ 如何交互的详细信息。
您写道您想要“直接在 C++ 中创建对象”。 这相当困难,因为至少您必须将对象实例化期间发生的任何 Python 异常转换为 C++ 异常。 您可以尝试查看 Boost::Python 以获得有关此任务的一些帮助。 或者您可以使用两阶段初始化。 ;)
The documentation for these is at http://docs.python.org/3.0/c-api/typeobj.html and
http://docs.python.org/3.0/extending/newtypes.html describes how to make your own type.
tp_alloc does the low-level memory allocation for the instance. This is equivalent to malloc(), plus initialize the refcnt to 1. Python has it's own allocator, PyType_GenericAlloc, but a type can implement a specialized allocator.
tp_new is the same as Python's __new__. It's usually used for immutable objects where the data is stored in the instance itself, as compared to a pointer to data. For example, strings and tuples store their data in the instance, instead of using a char * or a PyTuple *.
For this case, tp_new figures out how much memory is needed, based on the input parameters, and calls tp_alloc to get the memory, then initializes the essential fields. tp_new does not need to call tp_alloc. It can for example return a cached object.
tp_init is the same as Python's __init__. Most of your initialization should be in this function.
The distinction between __new__ and __init__ is called two-stage initialization, or two-phase initialization.
You say "c++ just has new" but that's not correct. tp_alloc corresponds a custom arena allocator in C++, __new__ corresponds to a custom type allocator (a factory function), and __init__ is more like the constructor. That last link discusses more about the parallels between C++ and Python style.
Also read http://www.python.org/download/releases/2.2/descrintro/ for details about how __new__ and __init__ interact.
You write that you want to "create the object directly in c++". That's rather difficult because at the least you'll have to convert any Python exceptions that occurred during object instantiation into a C++ exception. You might try looking at Boost::Python for some help with this task. Or you can use a two-phase initialization. ;)
我根本不知道Python API,但是如果Python将分配和初始化分开,你应该能够使用placement new。
例如:
I don't know the python APIs at all, but if python splits up allocation and initialization, you should be able to use placement new.
e.g.: