Python 的 C API 的 const 正确性

Posted

技术标签:

【中文标题】Python 的 C API 的 const 正确性【英文标题】:Const correctness of Python's C API 【发布时间】:2014-01-02 02:38:06 【问题描述】:

看来 Python C API 与字符数组的 const 正确性不一致。例如,PyImport_ImportFrozenModule 接受 char*,而 PyImport_ImportModule 接受 const char*

所有这一切的含义是,在我使用嵌入式 Python 解释器编写的 C++ 应用程序中,有时我必须将传递给 Python API 调用的字符串文字转换为 char*(而不是const char*),有时我不会。例如:

PyObject *os = PyImport_ImportModule("os"); // Works without the const_cast
PyObject *cwd = PyObject_CallMethod(os, const_cast<char*>("getcwd"), NULL); // Accepts char*, not const char*

如果我不对字符串文字执行const_cast&lt;char*&gt;(或(char*)),我会收到关于将字符串文字转换为char* 的编译器警告。

这是我的问题:

    让某些函数不采用const char* 是否有优势/原因(和/或为什么 Python API 在这方面不一致)?我的理解是,如果函数可以接受字符串文字,它就不能改变 char* 所以 const 修饰符只会加强这一点。我也相信const 的区别对于 C(为其编写 API)并不像在 C++ 中那么重要(如果我错了,请纠正我......我的强项是 python,而不是 C/C++)。 Python API 缺乏“const 正确性”是因为它在 C 中根本不那么重要吗? (有一个 2000 年的 old thread on the python mailing list 提出了同样的问题,但它似乎没有去任何地方,这暗示原因可能是由于某些编译器不支持 const。由于现在许多函数都有 const char*,这似乎不再适用)

    因为我对 C++ 的理解有限,我不确定我是否要正确地转换字符串文字。在我看来,我可以选择以下任何一种(我目前正在做第一种):

    // Method 1) Use const_cast<char*>
    PyImport_ImportFrozenModule(const_cast<char*>("mymodule"));
    
    // Method 2) Use (char*)
    PyImport_ImportFrozenModule((char*) "mymodule");
    
    // Method 3) Use char array
    char mod[] = "mymodule";
    PyImport_ImportFrozenModule(mod);
    

    使用哪种方法最好?


更新:

看起来 Python3 分支正在慢慢尝试修复 const 正确性问题。例如,我在上面用作示例的 PyImport_ImportFrozenModule 函数现在在 Python 3.4 中采用 const char*,但仍有一些函数只采用 char*,例如 PyLong_FromString。

【问题讨论】:

Here's a thread from 2002 on the python-dev mailing list on this topic。 C API 最初是在没有 const 正确性的情况下创建的,有人想通过在任何地方添加它来解决这个问题。 Guido 拒绝,因为它会破坏第 3 方扩展。 Here's another 更新的线程。似乎最初创建 API 时并没有考虑到这一点。多年来,人们一直在某些地方添加它,但并非在所有地方都这样做。 @dano 这些是我以前没有找到的很棒的链接!所以听起来只有 C++ 程序员会关心这个(以避免编译器警告),这还不足以尝试修复它。 :( @dano:您应该做出回答,以便我们投票! :) @EthanFurman 当然,我刚刚添加了一个。 【参考方案1】:

根据 python-dev 的一些邮件列表对话,看起来最初的 API 只是没有考虑到 const 的正确性,可能只是因为 Guido 没有考虑它。一直追溯到 2002 年,someone asked 如果有任何希望通过添加 const 正确性来解决这个问题,抱怨总是不得不这样做很痛苦:

somefunc(const char* modulename, const char* key)

    ... PyImport_ImportModule(const_cast<char*>(modulename)) ...

Guido Van Rossum(Python 的创建者)replied(强调我的):

我之前从未尝试强制执行 const 正确性,但我听说过 关于这个的恐怖故事已经够多了。问题是它打破了3rd 派对扩展左右,修复这些并不总是那么容易。 通常,每当您在某处添加 const 时,它最终都会传播 到其他一些 API,这也需要一个 const,它传播 到另一个需要 const 的 API,无穷无尽。

有更多的讨论,但没有 Guido 的支持,这个想法就死了。

快进九年,话题又来了。这次有人只是想知道为什么有些函数是 const 正确的,而另一些则不是。 Python核心开发者之一replied with this:

多年来,我们一直在许多地方添加 const。我觉得 只是错过了特定情况(即没有人关心添加 const 那里)。

似乎当它可以在不破坏向后兼容性的情况下完成时,常量正确性已被添加到 C API 中的许多地方(对于 Python 3,在它打破了与 Python 2 的向后兼容性),但从来没有真正的全球努力来修复它无处不在。所以 Python 3 的情况要好一些,但即使是现在,整个 API 也可能不是 const 正确的。

我不认为 Python 社区有任何首选方法来处理不正确的调用转换 const-正确(在官方 C-API style guide 中没有提到它),可能是因为没有有很多人通过 C++ 代码与 C-API 交互。不过,我会说,从纯 C++ 最佳实践的角度来看,首选方法是首选。 (我绝不是 C++ 专家,所以对此持保留态度)。

【讨论】:

【参考方案2】:

让某些功能不采用const char* 是否有优势/原因?

没有。看起来像是图书馆设计的疏忽,或者像你说的,遗留问题。不过,他们至少可以让它保持一致!

我的理解是,如果函数可以接受字符串文字,它就不能改变 char* 所以 const 修饰符只会加强这一点。

没错。他们的文档还应该指定函数参数(或者,更确切地说,参数的指针)在函数调用期间不应被修改;可惜它目前没有这样说。

我还认为,const 的区别对于 C(为其编写 API)并不像在 C++ 中那么重要。

嗯,不是真的,至少据我所知。

按照我的看法,我可以选择以下任何一种(我目前正在做第一种)

(好)

使用哪种方法最好?

const_cast 至少会确保您修改const-ness,所以如果您必须选择我会选择它。但是,真的,我不会太在意这个。

【讨论】:

以上是关于Python 的 C API 的 const 正确性的主要内容,如果未能解决你的问题,请参考以下文章

使用 C 库时 C++ 中的 const 正确性

C 中的泛型编程 - void*- const 正确性

如何使用 ctypes 在 python 中正确包装 C API?

VB中const用法

以下关于指针的说法,正确的是( ) A.int *const p与int const *p等价 B.const int *p与int *const p等价 C.const int *p与int c

Python C Api 和正确处理 C++ 类中的内存