【问题标题】:Static initialization with pointer to extern variable使用指向外部变量的指针进行静态初始化
【发布时间】:2014-09-15 05:52:36
【问题描述】:

我想了解 Python 导入系统的内部结构,包括粗略之处。在 Python C API documentation 中,有这样一个粗略的引用:

这非常重要,我们将把它的顶部分开 更进一步:

PyObject_HEAD_INIT(NULL)

这条线有点儿毛病;我们想要什么 写的是:

PyObject_HEAD_INIT(&PyType_Type)

因为类型对象的类型是 “类型”,但这并不严格符合 C 和某些编译器 抱怨。

为什么这不严格符合 C?为什么有些编译器会毫无怨言地接受这一点而其他编译器不接受?

我现在认为以下内容具有误导性,请跳至“实质性编辑”

我认为向下滚动页面是一个线索。这句话是关于初始化结构的另一个成员,但听起来是同一个问题,这次解释一下。

我们只想将其分配给 tp_new 插槽,但我们不能,因为 为了可移植性,在某些平台或编译器上,我们不能静态地 用另一个 C 中定义的函数初始化结构成员 模块

这仍然让我有点困惑,部分原因是“模块”的奇怪词选择。我认为第二个引用的意思是说依赖于单独编译单元中函数调用的静态初始化是一个非标准扩展。我还是不明白为什么会这样。这是第一个引用中发生的情况吗?

实质性编辑:

建议在PyTypeObject 实例初始化的最顶端使用PyObject_HEAD_INIT(NULL)

PyTypeObject 的定义如下:

typedef struct _typeobject {
    PyObject_VAR_HEAD
    const char *tp_name; /* For printing, in format "<module>.<name>" */
    Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */

    /* Methods to implement standard operations */

    destructor tp_dealloc;

/*... lots more ... */

} PyTypeObject;

PyObject_HEAD_INIT(NULL) 宏用于初始化 PyTypeObject 实例的顶部。 PyTypeObject 定义的顶部由宏 PyObject_VAR_HEAD 创建。 PyObject_VAR_HEAD 是:

/* PyObject_VAR_HEAD defines the initial segment of all variable-size
 * container objects.  These end with a declaration of an array with 1
 * element, but enough space is malloc'ed so that the array actually
 * has room for ob_size elements.  Note that ob_size is an element count,
 * not necessarily a byte count.
 */
#define PyObject_VAR_HEAD               \
    PyObject_HEAD                       \
    Py_ssize_t ob_size; /* Number of items in variable part */
#define Py_INVALID_SIZE (Py_ssize_t)-1

反过来,PyObject_HEAD 扩展为:

/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD                   \
    _PyObject_HEAD_EXTRA                \
    Py_ssize_t ob_refcnt;               \
    struct _typeobject *ob_type;

_PyObject_HEAD_EXTRA 仅用于调试构建,通常扩展为空。 PyObject_HEAD_INIT 宏初始化的成员是 ob_refcnt 和 ob_type。 ob_type 是我们想用&amp;PyType_Type 初始化的类型,但我们被告知这会违反C 标准。 ob_type 指向一个 _typeobject,它被 typedef 为 PyTypeObject(与我们尝试初始化的结构相同)。我们使用 PyObject_HEAD_INIT 宏来初始化这两个值,扩展如下:

#define PyObject_HEAD_INIT(type)        \
    _PyObject_EXTRA_INIT                \
    1, type,

所以我们从 1 开始引用计数,并将成员指针设置为类型参数中的任何内容。 Python 文档说我们不能将类型参数设置为 PyType_Type 的地址,因为这不是严格的标准 C,所以我们选择 NULL。

PyType_Type 在下面几行的同一翻译单元中声明。

PyAPI_DATA(PyTypeObject) PyType_Type; /* built-in 'type' */

PyAPI_DATA 在别处定义。它有几个不同的条件定义。

#define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE
#define PyAPI_DATA(RTYPE) extern RTYPE

所以 Python API 文档说我们想要初始化一个 PyTypeObject 的实例,该实例使用指向先前声明的 PyTypeObject 的指针,该指针使用 extern 限定符声明。这会违反 C 标准中的哪些内容?

PyType_Type 的初始化发生在 .c 文件中。如上所述,初始化 PyTypeObject 的典型 Python 扩展将由使用此初始化编译的代码动态加载:

PyTypeObject PyType_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "type",                                     /* tp_name */
    sizeof(PyHeapTypeObject),                   /* tp_basicsize */
    sizeof(PyMemberDef),                        /* tp_itemsize */
    (destructor)type_dealloc,                   /* tp_dealloc */

/* ... lots more ... */
}

【问题讨论】:

  • PyObject_VAR_HEAD 的定义是什么,PyObject_HEAD_INIT(...) 是它的初始化器?
  • 我认为这偏离了方向,但定义是#define PyObject_VAR_HEAD \ PyObject_HEAD \ Py_ssize_t ob_size;我可以在几分钟内通过宏隧道并添加一个。
  • “我认为这偏离了方向”——那么,你缺乏基本的理解。您需要知道的是&amp;PyType_Type(或NULL)正在初始化的事物的类型。所以接下来你应该发布 PyObject_HEAD 的定义。如果这是根据另一个宏定义的,请发布其定义,依此类推。除非你真的不需要任何帮助。
  • “我认为第二个引用的意思是说依赖于单独编译单元中的函数调用的静态初始化是非标准扩展。” -- 不,不是函数的调用,函数的地址。但这没有什么不标准的。但是,在编写高度可移植的代码时,标准并不重要……您必须考虑标准编译器……尽管我不知道哪个编译器有这样的限制。跨度>
  • 我试图改进这个问题并提供所有需要的定义。还有什么帮助吗?

标签: c portability linkage python-internals


【解决方案1】:
PyObject_HEAD_INIT(&PyType_Type)

生产

1, &PyType_Type

初始化字段

Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;

PyType_Type 是用PyAPI_DATA(PyTypeObject) PyType_Type 定义的,它产生

extern PyTypeObject PyType_Type;

可能带有 __declspec 限定符。 PyTypeObject 是 struct _typeobject 的 typedef,所以我们有

extern struct _typeobject PyType_Type;

所以PyObject_HEAD_INIT(&amp;PyType_Type) 将使用struct _typeobject* 初始化struct _typeobject* ob_type 字段...这当然是有效的C,所以我不明白他们为什么说它不是。

【讨论】:

    【解决方案2】:

    我在 Python 源代码的其他地方找到了对此的解释。

    /* We link this module statically for convenience.  If compiled as a shared
       library instead, some compilers don't allow addresses of Python objects
       defined in other libraries to be used in static initializers here.  The
       DEFERRED_ADDRESS macro is used to tag the slots where such addresses
       appear; the module init function must fill in the tagged slots at runtime.
       The argument is for documentation -- the macro ignores it.
    */
    #define DEFERRED_ADDRESS(ADDR) 0
    

    然后在NULL出现在OP顶部的地方使用宏。

    PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
    

    【讨论】:

      猜你喜欢
      • 2015-05-08
      • 1970-01-01
      • 2021-03-12
      • 2017-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多