不同编程语言和调用约定
根据维基百科的说法:
不同的编程语言有不同的调用约定,而不同的平台(比如CPU架构加上操作系统)也可能有不同的调用方式。这有时候会在结合用多种语言写的模块时造成问题。
那么,当我在Python中调用C/C++函数(从.so/.dll文件导出的)时,我需要小心吗?如果需要,我应该注意些什么呢?
4 个回答
C和C++有不同的调用约定。简单来说,C语言不能直接调用C++的函数,但C++可以调用C语言的函数,并且可以在C++中实现C语言的函数。也就是说,你可以在C++中用extern "C"
来声明函数。我不太清楚Python的绑定是怎么调用C++的,但在调用C和C++的函数时,你需要注意它们各自的调用约定。通常,在不同语言之间调用时,会使用C的调用约定。这可能意味着你需要用C风格的约定来写一个包装函数,以便调用C++风格的函数。
在Python和C之间调用是一个已经解决的问题,所以你一般不需要担心这个,尤其是因为Python本身就是用C写的。
这里提到的问题更多是出现在多个语言在同一个平台上独立开发的情况。比如,曾经在C和FORTRAN之间、C和Pascal之间调用时就会遇到麻烦,那时候这三种语言的地位差不多。老款的Mac工具箱主要是用汇编语言写的,并且使用了Pascal的调用规则,而早期的应用开发者使用的是Borland Pascal。不过后来像Symantec的THINK C这样的C编译器出现了,这些程序员就不得不特别关注如何转换参数类型和字符串的规则(Pascal字符串在开头有一个长度字节,而C字符串在结尾有一个0)。
调用约定在从Python调用C时并不是问题,因为Python解释器本身就是用C写的,所以它使用的调用约定和C代码是一样的。
但是,当你把不同编译语言写的代码结合在一起时,比如C和Ada,这就成了一个问题。
通常,C是系统编程语言,因此被视为标准。这意味着任何语言的编译器通常都会特别支持与C的互操作性。你可以在这里查看C和Ada之间互操作性的例子。如果没有特别的支持,就必须在汇编层面写一些包装代码。
如果你需要从Python调用C++或Ada,你需要遵循两个步骤。第一步是为C++或Ada函数写一个C的包装。第二步是从Python调用这个C的包装。
举个例子,假设你有以下的C++类。
class Foo
{
public:
Foo ();
int bar ();
...
};
如果你想让它在Python中可用,首先需要把它用C包装起来:
extern "C" {
typedef void *FooPtr;
FooPtr foo_new () { return (FooPtr)new Foo(); }
int foo_bar (FooPtr foo) { return ((Foo*)foo)->bar(); }
...
}
然后你就可以从Python调用它了。(注意,实际上有一些工具可以自动化这个过程,比如Boost.Python)。
需要注意的是,写一个包装有两个方面:转换调用约定和转换类型表示。后者通常是最困难的,因为一旦超出了基本类型,各种语言对等价类型的表示方式往往差别很大。