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<char*>
(或(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 正确性的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ctypes 在 python 中正确包装 C API?
以下关于指针的说法,正确的是( ) A.int *const p与int const *p等价 B.const int *p与int *const p等价 C.const int *p与int c