了解外部函数接口(FFI)和语言绑定

53
混合不同的编程语言长期以来一直是我不太理解的事情。根据这篇维基百科文章,可以通过以下几种方式实现外部函数接口(FFI):
  1. 要求以特定方式指定或实现可在主机语言中调用的客户端语言函数;通常使用某种兼容性库。
  2. 使用工具自动“包装”客户端语言函数,并使用适当的粘合代码执行任何必要的翻译。
  3. 使用包装器库
  4. 限制跨语言使用的主机语言功能集。例如,从C调用的C++函数通常不能包括引用参数或抛出异常。
我的问题:
  1. 第一种、第二种和第三种方式有什么区别?在我看来,它们都是将所调用语言的代码编译成某些库,其中包括目标文件和头文件,然后由调用语言进行调用。

  2. 其中一篇引用的来源说,实现FFI可以通过几种方式来完成:

    • 要求目标语言中被调用的函数实现特定的协议。
    • 实现一个包装库,该库采用给定的低级语言函数,并使用代码对其进行“包装”,以执行数据转换到/从高级语言约定。
    • 要求声明为本地的函数使用高级功能的子集(与低级语言兼容)。

    我想知道链接源中的第一种方式是否与维基百科中的第一种方式相同?

    这个来源中的第三种方法是什么意思?它是否对应于维基百科中的第四种方法?

  3. 同一来源中,当比较列出的三种方式时,似乎表明填补两种语言之间差距的工作逐渐从所调用的语言转移到了所调用的语言。我想知道如何理解这一点?这种转移对维基百科中的四种方式也适用吗?

  4. 语言绑定和FFI是否是等效的概念?它们如何相关和有何不同?

    从编程语言到库或操作系统服务的绑定是提供该语言中该服务的API。

  5. 我想知道引文中的每个示例属于维基百科或来源中的哪种方式?

1个回答

29

也许一个具体的例子会有所帮助。让我们以 Python 为主语言,以 C 为客语言。这意味着 Python 将调用 C 函数。

  1. 第一种选择是以特定的方式编写 C 库。在 Python 的情况下,标准方法是将 C 函数编写为具有 Py_Object * 作为第一个参数等条件。例如(从这里):
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;
    
    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
}

是一个可从Python调用的C函数。为了使其正常工作,库必须考虑到Python的兼容性。

  1. 如果您想使用已经存在的C库,则需要另一种选项。其中之一是拥有一个工具,可以生成适合主机语言使用的格式的现有库的包装器。例如Swig,它可以用于多种语言。给定一个现有的C库,您可以使用swig有效地生成调用现有库的C代码,同时符合Python约定。请参见示例以构建Python模块。

  2. 使用已经存在的C库的另一种选择是从有效地包装运行时调用的Python库中调用它,例如ctypes。虽然在选项2中需要编译,但这次不需要。

另外一件事是,有很多选项(它们有重叠)可以在一种语言中调用另一种语言的函数。有FFI(据我所知相当于语言绑定),通常指在同一进程中(作为同一可执行文件的一部分,可以这么说)之间进行调用的多种语言之间的调用,还有进程间通信手段(本地和网络)。像CORBA、Web服务(SOAP或REST)、COM+和远程过程调用等东西属于第二类,不被视为FFI。实际上,它们大多数情况下都没有规定要在通信的任何一侧使用特定的语言。我会将它们松散地归为IPC(进程间通信)选项,尽管在基于网络的API(如CORBA和SOAP)的情况下,这是简化了。
看看你的列表,我会提出以下观点:
常见对象请求代理架构:IPC,而非FFI 在C++中调用C,通过C++中的"extern "C""声明禁用名称修饰。 在Matlab中调用C,使用MATLAB接口共享库选项3(类似于ctypes) 在Matlab中调用C,通过创建C/C++语言MEX文件选项2(类似于swig) 在C中调用Matlab,通过mcc编译器选项2(类似于swig) 在Java中调用C++,通过JNI,并通过JNI在C++中调用Java选项3(类似于ctypes) 在其他语言中调用C/C++,使用SWIG选项2(类似于swig) 在Python中调用C,通过Ctypes选项3(类似于ctypes) Cython选项2(类似于swig) 在Python中调用R,通过RPy选项3(部分类似于ctypes,部分关于数据交换而非FFI)
接下来的两个并不是所谓的外部函数接口(FFI),因为FFI是关于两种编程语言之间的交互,应该能够使得任何一个语言的库(在适当的限制下)可以被另一个语言使用。某个特定的库可以从一个语言中访问,并不代表它就是FFI。
  • 各种语言对OpenGL的编程语言绑定
  • 各种语言对C库的绑定

+1 不错的回答。需要注意的是,“通过创建MEX文件在MATLAB中调用C”更像是“选项1”,它相当于使用其C API编写Python扩展。您可以创建一个常规共享库,其中包含一个特殊的网关例程,该例程接收mxArray *参数。至于“使用mcc编译器在C中调用MATLAB”,这实际上不是FFI,因为这只是C代码调用其他C代码(MCC编译器生成常规共享库)。 - Amro
另一个未被提及的选项是“使用MATLAB引擎在C中调用MATLAB”,这类似于“Python嵌入”以延续类比(与使用JNI接口在C中调用Java相同)。 - Amro
Muhammad,谢谢!我用了很长时间才回来理解你的大部分回复。我的道歉。“接下来的两个根本不是所谓的外部函数接口...一个特定的库从一种语言中可访问,并不能成为FFI”。例如,如果我没错的话,OpenGL(或在我的帖子中链接的Cairo)是由C库实现的,那么它在另一种语言(比如Python)中的绑定是如何实现的?也是你在帖子开头列出的三种FFI方法之一吗? - Tim
此外,“通过JNI在Java中调用C++,以及通过JNI在C++中调用Java的选项3(类似于ctypes)”,我认为JNI就像选项1一样。 - Tim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接