Python 正在使用C代码在C++;因为生命?

Python 正在使用C代码在C++;因为生命?,python,c++,c,cpython,python-extensions,Python,C++,C,Cpython,Python Extensions,有很多教程介绍如何创建Python的C扩展,这些扩展引入了一种新类型。一个例子: 这通常归结为创建一个结构,如: struct Example { PyObject_HEAD // Extra members }; 然后通过隐式或显式定义函数指针映射将其注册到模块中。与生存期相关的是tp_alloc、tp_new、tp_init、tp_free、tp_dealloc 从我对其工作原理的理解来看,PyObject\u HEAD扩展为PyObject ob\u base使Examp

有很多教程介绍如何创建Python的C扩展,这些扩展引入了一种新类型。一个例子:

这通常归结为创建一个结构,如:

struct Example
{
    PyObject_HEAD
    // Extra members
};
然后通过隐式或显式定义函数指针映射将其注册到模块中。与生存期相关的是
tp_alloc、tp_new、tp_init、tp_free、tp_dealloc

从我对其工作原理的理解来看,
PyObject\u HEAD
扩展为
PyObject ob\u base
使
Example*
PyObject*
可转换(如果它是第一个成员,我想会有一些特殊的措辞),因此所有代码都接受
PyObject*
并可以像
struct Example:public PyObject{}一样使用它已被使用。到目前为止一切都很好

但现在的问题是,如果
示例
:经过一些挖掘,似乎发生了以下情况:

  • tp_new
    通过要创建的对象的“type_info”(函数指针映射)调用
  • 这将调用
    tp\u alloc
    ,默认为(基本上)
    malloc
  • 然后使用
    tp_new
    中的内存指针调用
    tp_init
    ,例如填充ref计数器
  • 销毁时调用
    tp\u dealloc
  • 这将调用
    tp\u free
    (基本上是
    free
因此,明显缺少的是对构造函数和析构函数的调用,如果结构是POD,那么在实践中这是很好的

然而,最近的C++标准已经明确指出,仅仅对对象进行错位是不够的,参见例如“代码> STD::Launt和相关讨论。

因此编译C++扩展为ub?如果不是的话,我想对豆荚有一个特殊的规定,所以这些豆荚是安全的,不是吗?是否有需要澄清的参考资料

是否有关于以性能方式创建非POD类型的安全方法的文档?即,不添加指向上方指向非POD对象的
示例
POD对象的指针,该非POD对象随后通过
新建
或类似方式创建

从对的描述和回答中,我可以提取出
tpu new
可以执行
new
并返回该值,或者调用
tp\u alloc
并在返回的内存中执行新的放置并返回该值。在我看来,这是一个“只有在绝对必要的情况下才能进一步初始化”的要求
tp_dealloc
然后调用析构函数并转发到
tp_free
。听起来不错,但如果返回的内存对齐错误,这可能会有问题吗

是否保证
tp\u new
tp\u dealoc
只调用一次

根据上述描述,非Python程序员的一些伪代码:

PyObject* tp_alloc(size_t n){ return malloc(n); }
PyObject* tp_new(PyTypeObject* typeInfo){ return typeinfo->tp_alloc(typeinfo->object_size); }
PyObject* tp_init(PyTypeObject* typeInfo, PyObject* o){ o->typeInfo = typeInfo; o->refCnt = 1; return o }
void tp_dealloc(PyObject* o){ o->typeInfo->tp_free(o); }
void tp_free(void* m){ free(m); }

//User code
struct Example
{
    PyObject obj;
    // Extra members
};
void register(){
  PyTypeObject info = {.tp_alloc = tp_alloc, .tp_new = tp_new, .object_size = sizeof(Example), ...}
  PythonRegister("Example", info);
}
请注意,这是简化的。每当创建/使用一种类型的名称“Example”时,Python就会使用
info
对象。您可以覆盖所有函数,并在
Example*
PyObject*
之间进行转换,尽管没有继承,因为它们是“指针可相互转换的”:

一个是标准布局类对象,另一个是该对象的第一个非静态数据成员

我现在的想法是通过如下方式覆盖默认的
tp_new

PyObject* Example_new(PyTypeObject* typeInfo){ return new(typeinfo->tp_alloc(typeinfo->object_size)) Example; }

<>我想知道这是否是必需的和有效的。< /P>请注意,在C++中不再存在POD。它在C++11中被删除,并替换为标准布局类。你可以从中得到一些保证,但是C++严格的别名规则仍然会影响你。你可以做的是编译C代码作为C,并将它链接到C++项目中,是吗?我已经看到
是在C++11中添加的,它是“标准布局+琐碎的”。但确切的名字并不重要,你似乎明白我的意思。关于“指针可相互转换”这一点,是不是涵盖了这种情况?我也不确定这两步编译是否真的可行。setuptools的作用是调用
gcc。。。MCP..PTC-STD= C++ 11 ,它调用C编译器,但我假设编译代码为C++。在1个扩展中改变语言似乎是不可能的,特别是当我需要访问C中的C++类时,对于BddIGIN认为,严格的别名规则与类型的“琐碎”不同,看起来你也在咀嚼这些规则。另外,添加更多的代码可能会帮助不熟悉Python的人(比如我)理解Python的行为。值得一提的是,在C++20中,这并不是一个问题。假设所讨论的类型是“行为良好的”。@Flamefire啊,看起来我的标准错了。C++11是新需求的开始,在C++20中被弃用。没有看到具体的代码,我只能假设C方式不是C++方式,而是UB,直到(可能取决于代码)C++ 20.注意POD不再存在于C++中。它在C++11中被删除,并替换为标准布局类。你可以从中得到一些保证,但是C++严格的别名规则仍然会影响你。你可以做的是编译C代码作为C,并将它链接到C++项目中,是吗?我已经看到
是在C++11中添加的,它是“标准布局+琐碎的”。但确切的名字并不重要,你似乎明白我的意思。关于“指针可相互转换”这一点,是不是涵盖了这种情况?我也不确定这两步编译是否真的可行。setuptools的作用是调用
gcc。。。MCP..PTC-STD= C++ 11 ,它调用C编译器,但我假设编译代码为C++。在1个扩展中改变语言似乎不可能,特别是当我需要访问C中的C++类时,对于BddIGIN认为,严格的别名规则不同于“琐碎”。