用一种语言编写的代码如何从另一种语言中调用?

时间:2022-09-11 20:45:18

This is a question that I've always wanted to know the answer, but never really asked.

这是一个我一直想知道答案的问题,但从未真正问过的问题。

How does code written by one language, particularly an interpreted language, get called by code written by a compiled language.

由一种语言(特别是一种解释语言)编写的代码如何被编译语言编写的代码调用?

For example, say I'm writing a game in C++ and I outsource some of the AI behavior to be written in Scheme. How does the code written in Scheme get to a point that is usable by the compiled C++ code? How is it used by the C++ source code, and how is it used by the C++ compiled code? Is there a difference in the way it's used?

例如,假设我正在用c++编写一个游戏,我将一些人工智能行为外包给Scheme。如何在Scheme中编写的代码达到可被编译的c++代码使用的程度?c++源代码如何使用它,c++编译代码如何使用它?它的使用方式有区别吗?

Related

How do multiple-languages interact in one project?

多语言如何在一个项目中相互作用?

7 个解决方案

#1


13  

There is no single answer to the question that works everywhere. In general, the answer is that the two languages must agree on "something" -- a set or rules or a "calling protocol".

这个问题没有单一的答案。一般来说,答案是两种语言必须在“某物”上达成一致——一套或一套规则或一套“调用协议”。

In a high level, any protocol needs to specify three things:

在高层次上,任何协议都需要指定三件事:

  • "discovery": how to find about each other.
  • “发现”:如何发现彼此。
  • "linking": How to make the connection (after they know about each other).
  • “链接”:如何建立联系(在他们了解彼此之后)。
  • "Invocation": How to actually make requests to each other.
  • “调用”:如何实际向彼此发出请求。

The details depend heavily on the protocol itself.

细节在很大程度上取决于协议本身。

Sometimes the two languages conspire to work together. Sometimes the two languages agree to support some outside-defined protocol. These days, the OS or the "runtime environment" (.NET and Java) is often involved as well. Sometimes the ability only goes one way ("A" can call "B", but "B" cannot call "A").

有时这两种语言合二为一。有时两种语言都同意支持一些由外部定义的协议。现在,操作系统或“运行时环境”(运行时环境)。NET和Java)也经常涉及到。有时这种能力只会朝一个方向发展(“A”可以叫“B”,而“B”不能叫“A”)。

Notice that this is the same problem that any language faces when communicating with the OS. The Linux kernel is not written in Scheme, you know!

请注意,这与任何语言在与操作系统通信时所面临的问题相同。Linux内核不是用Scheme编写的,你知道的!

Let's see some typical answers from the world of Windows:

让我们看看Windows世界的一些典型答案:

  • C with C++: C++ uses a contorted ("mangled") variation of the "C protocol". C++ can call into C, and C can call into C++ (although the names can be quite messy sometimes and it might need external help translating the names). This is not just Windows; it's generally true in all platforms that support both. Most popular OS's use a "C protocol" as well.

    使用c++的C使用了“C协议”的扭曲(“损坏”)变体。c++可以调用C语言,C语言可以调用c++语言(虽然有时候名称可能会很混乱,可能需要外部帮助来翻译这些名称)。这不仅仅是Windows;在所有支持两者的平台中,通常都是这样。最受欢迎的操作系统也使用“C协议”。

  • VB6 vs. most languages: VB6's preferred method is the "COM protocol". Other languages must be able to write COM objects to be usable from VB6. VB6 can produce COM objects too (although not every possible variation of COM objects).

    VB6与大多数语言:VB6的首选方法是“COM协议”。其他语言必须能够编写从VB6可用的COM对象。VB6也可以生成COM对象(虽然不是COM对象的所有可能变化)。

    VB6 can also talk a very limited variation of the "C protocol", and then only to make calls outside: it cannot create objects that can be talked to directly via the "C protocol".

    VB6还可以对“C协议”进行非常有限的修改,然后只在外部进行调用:它不能创建可以通过“C协议”直接进行对话的对象。

  • .NET languages: All .NET languages communicate compile to the same low-level language (IL). The runtime manages the communication and from that point of view, they all look like the same language.

    . net语言:所有。net语言都将编译与相同的底层语言(IL)进行通信。运行时管理通信,从这个角度来看,它们看起来都是相同的语言。

  • VBScript vs. other languages: VBScript can only talk a subset of the COM protocol.

    VBScript和其他语言:VBScript只能讨论COM协议的一个子集。

One more note: SOAP "Web Services" is really a "calling protocol" as well, like many other web-based protocol that are becoming popular. After all, it's all about talking to code written in a different language (and running in a different box at that!)

还有一点要注意:SOAP“Web服务”实际上也是一种“调用协议”,就像许多其他正在流行的基于Web的协议一样。毕竟,这一切都是关于与用不同语言编写的代码对话(并在不同的框中运行!)

#2


4  

Typically the C++ code will invoke an interpreter for the scripting language. The degree of interaction between the compiled and scripting code is dependent on the interpreter but there's always a way to pass data between the two. Depending on the interpreter, it may be possible to manipulate the objects on one side from the other side, such as a C++ function calling a method on a Ruby object. There may even be a way to control execution of one from the other.

通常,c++代码会为脚本语言调用解释器。编译代码和脚本代码之间的交互程度依赖于解释器,但始终有一种方法可以在两者之间传递数据。根据解释器的不同,可以从另一端操作对象,例如c++函数调用Ruby对象上的方法。甚至可能有一种方法可以控制从一个执行到另一个执行。

#3


4  

There is a protocol for how modules communicate. Here is a high-level, broad overview of how it works:

有一个模块如何通信的协议。以下是它如何工作的一个高级的、广泛的概述:

  1. A library is created for code you want to 'share'. These are commonly called DLLs or SOs depending on your platform.
  2. 为要“共享”的代码创建一个库。根据您的平台,这些通常称为dll或SOs。
  3. Each function you want to expose (entry point) will be available to the outside world to bind to. There are protocols to how to bind such as the calling convention which specifies the order the parameters are passed, who cleans up the stack, how many parameters get stored in registers and which ones, etc. See cdecl, stdcall, etc for examples of calling conventions here.
  4. 您希望公开的每个函数(入口点)都可以被外部世界绑定。有一些协议是关于如何绑定的,比如调用约定,它指定了参数传递的顺序、谁清理堆栈、有多少参数存储在寄存器中以及哪些参数等等。
  5. The calling module will then either statically or dynamically bind to the shared library.
  6. 然后调用模块将静态地或动态地绑定到共享库。
  7. Once your calling library is bound to the shared library it can then specify it wants to bind to a particular entry point. This is generally done by name, however most platforms also offer the option of binding by index (faster, yet more brittle if your module changes and entry points are reordered).
  8. 一旦您的调用库被绑定到共享库,它就可以指定要绑定到特定的入口点。这通常是通过名称完成的,但是大多数平台也提供按索引进行绑定的选项(如果您的模块发生更改并且入口点被重新排序,那么绑定速度会更快,但更脆弱)。
  9. You will also generally declare the function you want to call in your module somewhere so that your language can do static type checking, knows what the calling convention is etc.
  10. 您通常还会在模块中某处声明要调用的函数,以便您的语言可以执行静态类型检查,知道调用约定是什么等等。

For your scenario of calling Scheme from C++, the Scheme interpreter most likely exports a function that dynamically binds to a Scheme function/object and calls that. If the Scheme module is compiled it probably has the option of exporting an entry point so your C++ module could bind to that. I am not very familiar with Scheme so someone else can probably answer the specifics of that particular binding better than I.

对于从c++调用Scheme的场景,Scheme解释器很可能会导出一个函数,该函数动态地绑定到Scheme函数/对象并调用它。如果Scheme模块被编译,那么它可能可以导出一个入口点,以便您的c++模块可以绑定到这个入口点。我不太熟悉Scheme,所以其他人可能会比我更好地回答特定绑定的细节。

#4


2  

You can also integrate the two environments without having to compile the interpreter's library inside your executable. You keep your exe and the Scheme exe as separate programs on your system. From your main exe you can write your Scheme code to a file then use system() or exec() to run the scheme interpreter. You then parse the output of the scheme interpreter.

您还可以集成这两个环境,而不必在可执行文件中编译解释器的库。您保留您的exe和Scheme exe作为您系统上的独立程序。从您的主要exe,您可以将您的Scheme代码写入文件,然后使用system()或exec()来运行Scheme解释器。然后解析scheme解释器的输出。

The approach suggested above keeps the exes separate and you do not have to worry about 3rd party dependencies, they can be significant. Also problems stay contained in one exe or another.

上面建议的方法将前任分开,您不必担心第三方的依赖性,它们可能很重要。同样,问题也存在于一个或另一个exe中。

If running a separate exe does not satisfy your performance requirements you can devise a protocol where the Scheme interpreter becomes a server. You need to write some Scheme functions that wait for input on a socket or file, eval that input then output the result to the same socket or a different file. Another iteration of this is to look at existing servers that may be running your interpreter already, for example apache has modules that allows code to be written in many languages.

如果运行一个单独的exe不满足您的性能要求,您可以设计一个协议,使Scheme解释器成为一个服务器。您需要编写一些Scheme函数来等待套接字或文件上的输入,然后对输入进行eval,然后将结果输出到相同的套接字或不同的文件。另一个迭代是查看可能已经在运行解释器的现有服务器,例如,apache有允许用多种语言编写代码的模块。

#5


1  

If you're actually looking for tools to do such a thing, a la Adam's response, see swig.

如果你正在寻找工具来做这样的事情,请参阅swig。

#6


1  

From a theoretical point of view, when program A need to use resources(class/functions/etc) from program B, it's about passing in some information from A to B, and get some information back or some actions performed. So there needs to be a way provided by B that allows A to pass in information and get result.

从理论的角度来看,当程序a需要使用程序B的资源(类/函数等)时,它是关于将一些信息从程序a传递到程序B,并获得一些信息或执行一些操作。所以B需要提供一种方法让a传递信息并得到结果。

In practice, it usually lies on the shoulder of languages to handle this process: the language B(program B is written in) will generate a protocol and make resources in B available in a predefined way, then language A(program A is written in) will provide some utility/framework to help invocate the exposed resources and get results following B's protocol.

在实践中,它通常位于肩膀的语言来处理这个过程:语言B(程序B写)将生成一个协议,使资源可用的预定义的方式,然后语言(写在程序)将提供一些实用工具/框架帮助引起公开的资源和得到结果后B的协议。

To be more specific to your question, for interpreted languages, the process is fairly universal, the protocol is normally among the lines of command line parameter, HTTP request and other ways of transmitting plain text. Take the first example, program B will receive a call from HTTP request as input, and then process the request from there on. The actual format of input is totally decided by program B.

更具体地说,对于解释语言来说,这个过程是相当普遍的,协议通常在命令行参数、HTTP请求和其他传输纯文本的方式之间。以第一个示例为例,程序B将接收来自HTTP请求的调用作为输入,然后从那里处理请求。输入的实际格式完全由程序B决定。

Things like SOAP and etc, are just a way to regulate programs to take input in a commonly agreed standard.

诸如SOAP之类的东西,只是一种管理程序的方法,以便在一个共同同意的标准中接受输入。

#7


1  

It's been a decade or so, but I did exactly this for my senior capstone (Well, I built a back-propogating neural network in C, and used a scheme program to teach it). The version of Scheme I was using had a compiler as well as an intepreter, and I was able to build it as a .o file. I don't know the version of scheme I was running, but it appears the RScheme will turn your scheme code into C.

已经有十年了,但我确实为我的高级指挥官做了这件事(嗯,我用C语言构建了一个反向的神经网络,并使用一个计划程序来教授它)。我使用的Scheme版本有一个编译器和一个intepreter,我可以把它构建成一个.o文件。我不知道我正在运行的方案的版本,但是看起来RScheme将把您的方案代码转换成C。

#1


13  

There is no single answer to the question that works everywhere. In general, the answer is that the two languages must agree on "something" -- a set or rules or a "calling protocol".

这个问题没有单一的答案。一般来说,答案是两种语言必须在“某物”上达成一致——一套或一套规则或一套“调用协议”。

In a high level, any protocol needs to specify three things:

在高层次上,任何协议都需要指定三件事:

  • "discovery": how to find about each other.
  • “发现”:如何发现彼此。
  • "linking": How to make the connection (after they know about each other).
  • “链接”:如何建立联系(在他们了解彼此之后)。
  • "Invocation": How to actually make requests to each other.
  • “调用”:如何实际向彼此发出请求。

The details depend heavily on the protocol itself.

细节在很大程度上取决于协议本身。

Sometimes the two languages conspire to work together. Sometimes the two languages agree to support some outside-defined protocol. These days, the OS or the "runtime environment" (.NET and Java) is often involved as well. Sometimes the ability only goes one way ("A" can call "B", but "B" cannot call "A").

有时这两种语言合二为一。有时两种语言都同意支持一些由外部定义的协议。现在,操作系统或“运行时环境”(运行时环境)。NET和Java)也经常涉及到。有时这种能力只会朝一个方向发展(“A”可以叫“B”,而“B”不能叫“A”)。

Notice that this is the same problem that any language faces when communicating with the OS. The Linux kernel is not written in Scheme, you know!

请注意,这与任何语言在与操作系统通信时所面临的问题相同。Linux内核不是用Scheme编写的,你知道的!

Let's see some typical answers from the world of Windows:

让我们看看Windows世界的一些典型答案:

  • C with C++: C++ uses a contorted ("mangled") variation of the "C protocol". C++ can call into C, and C can call into C++ (although the names can be quite messy sometimes and it might need external help translating the names). This is not just Windows; it's generally true in all platforms that support both. Most popular OS's use a "C protocol" as well.

    使用c++的C使用了“C协议”的扭曲(“损坏”)变体。c++可以调用C语言,C语言可以调用c++语言(虽然有时候名称可能会很混乱,可能需要外部帮助来翻译这些名称)。这不仅仅是Windows;在所有支持两者的平台中,通常都是这样。最受欢迎的操作系统也使用“C协议”。

  • VB6 vs. most languages: VB6's preferred method is the "COM protocol". Other languages must be able to write COM objects to be usable from VB6. VB6 can produce COM objects too (although not every possible variation of COM objects).

    VB6与大多数语言:VB6的首选方法是“COM协议”。其他语言必须能够编写从VB6可用的COM对象。VB6也可以生成COM对象(虽然不是COM对象的所有可能变化)。

    VB6 can also talk a very limited variation of the "C protocol", and then only to make calls outside: it cannot create objects that can be talked to directly via the "C protocol".

    VB6还可以对“C协议”进行非常有限的修改,然后只在外部进行调用:它不能创建可以通过“C协议”直接进行对话的对象。

  • .NET languages: All .NET languages communicate compile to the same low-level language (IL). The runtime manages the communication and from that point of view, they all look like the same language.

    . net语言:所有。net语言都将编译与相同的底层语言(IL)进行通信。运行时管理通信,从这个角度来看,它们看起来都是相同的语言。

  • VBScript vs. other languages: VBScript can only talk a subset of the COM protocol.

    VBScript和其他语言:VBScript只能讨论COM协议的一个子集。

One more note: SOAP "Web Services" is really a "calling protocol" as well, like many other web-based protocol that are becoming popular. After all, it's all about talking to code written in a different language (and running in a different box at that!)

还有一点要注意:SOAP“Web服务”实际上也是一种“调用协议”,就像许多其他正在流行的基于Web的协议一样。毕竟,这一切都是关于与用不同语言编写的代码对话(并在不同的框中运行!)

#2


4  

Typically the C++ code will invoke an interpreter for the scripting language. The degree of interaction between the compiled and scripting code is dependent on the interpreter but there's always a way to pass data between the two. Depending on the interpreter, it may be possible to manipulate the objects on one side from the other side, such as a C++ function calling a method on a Ruby object. There may even be a way to control execution of one from the other.

通常,c++代码会为脚本语言调用解释器。编译代码和脚本代码之间的交互程度依赖于解释器,但始终有一种方法可以在两者之间传递数据。根据解释器的不同,可以从另一端操作对象,例如c++函数调用Ruby对象上的方法。甚至可能有一种方法可以控制从一个执行到另一个执行。

#3


4  

There is a protocol for how modules communicate. Here is a high-level, broad overview of how it works:

有一个模块如何通信的协议。以下是它如何工作的一个高级的、广泛的概述:

  1. A library is created for code you want to 'share'. These are commonly called DLLs or SOs depending on your platform.
  2. 为要“共享”的代码创建一个库。根据您的平台,这些通常称为dll或SOs。
  3. Each function you want to expose (entry point) will be available to the outside world to bind to. There are protocols to how to bind such as the calling convention which specifies the order the parameters are passed, who cleans up the stack, how many parameters get stored in registers and which ones, etc. See cdecl, stdcall, etc for examples of calling conventions here.
  4. 您希望公开的每个函数(入口点)都可以被外部世界绑定。有一些协议是关于如何绑定的,比如调用约定,它指定了参数传递的顺序、谁清理堆栈、有多少参数存储在寄存器中以及哪些参数等等。
  5. The calling module will then either statically or dynamically bind to the shared library.
  6. 然后调用模块将静态地或动态地绑定到共享库。
  7. Once your calling library is bound to the shared library it can then specify it wants to bind to a particular entry point. This is generally done by name, however most platforms also offer the option of binding by index (faster, yet more brittle if your module changes and entry points are reordered).
  8. 一旦您的调用库被绑定到共享库,它就可以指定要绑定到特定的入口点。这通常是通过名称完成的,但是大多数平台也提供按索引进行绑定的选项(如果您的模块发生更改并且入口点被重新排序,那么绑定速度会更快,但更脆弱)。
  9. You will also generally declare the function you want to call in your module somewhere so that your language can do static type checking, knows what the calling convention is etc.
  10. 您通常还会在模块中某处声明要调用的函数,以便您的语言可以执行静态类型检查,知道调用约定是什么等等。

For your scenario of calling Scheme from C++, the Scheme interpreter most likely exports a function that dynamically binds to a Scheme function/object and calls that. If the Scheme module is compiled it probably has the option of exporting an entry point so your C++ module could bind to that. I am not very familiar with Scheme so someone else can probably answer the specifics of that particular binding better than I.

对于从c++调用Scheme的场景,Scheme解释器很可能会导出一个函数,该函数动态地绑定到Scheme函数/对象并调用它。如果Scheme模块被编译,那么它可能可以导出一个入口点,以便您的c++模块可以绑定到这个入口点。我不太熟悉Scheme,所以其他人可能会比我更好地回答特定绑定的细节。

#4


2  

You can also integrate the two environments without having to compile the interpreter's library inside your executable. You keep your exe and the Scheme exe as separate programs on your system. From your main exe you can write your Scheme code to a file then use system() or exec() to run the scheme interpreter. You then parse the output of the scheme interpreter.

您还可以集成这两个环境,而不必在可执行文件中编译解释器的库。您保留您的exe和Scheme exe作为您系统上的独立程序。从您的主要exe,您可以将您的Scheme代码写入文件,然后使用system()或exec()来运行Scheme解释器。然后解析scheme解释器的输出。

The approach suggested above keeps the exes separate and you do not have to worry about 3rd party dependencies, they can be significant. Also problems stay contained in one exe or another.

上面建议的方法将前任分开,您不必担心第三方的依赖性,它们可能很重要。同样,问题也存在于一个或另一个exe中。

If running a separate exe does not satisfy your performance requirements you can devise a protocol where the Scheme interpreter becomes a server. You need to write some Scheme functions that wait for input on a socket or file, eval that input then output the result to the same socket or a different file. Another iteration of this is to look at existing servers that may be running your interpreter already, for example apache has modules that allows code to be written in many languages.

如果运行一个单独的exe不满足您的性能要求,您可以设计一个协议,使Scheme解释器成为一个服务器。您需要编写一些Scheme函数来等待套接字或文件上的输入,然后对输入进行eval,然后将结果输出到相同的套接字或不同的文件。另一个迭代是查看可能已经在运行解释器的现有服务器,例如,apache有允许用多种语言编写代码的模块。

#5


1  

If you're actually looking for tools to do such a thing, a la Adam's response, see swig.

如果你正在寻找工具来做这样的事情,请参阅swig。

#6


1  

From a theoretical point of view, when program A need to use resources(class/functions/etc) from program B, it's about passing in some information from A to B, and get some information back or some actions performed. So there needs to be a way provided by B that allows A to pass in information and get result.

从理论的角度来看,当程序a需要使用程序B的资源(类/函数等)时,它是关于将一些信息从程序a传递到程序B,并获得一些信息或执行一些操作。所以B需要提供一种方法让a传递信息并得到结果。

In practice, it usually lies on the shoulder of languages to handle this process: the language B(program B is written in) will generate a protocol and make resources in B available in a predefined way, then language A(program A is written in) will provide some utility/framework to help invocate the exposed resources and get results following B's protocol.

在实践中,它通常位于肩膀的语言来处理这个过程:语言B(程序B写)将生成一个协议,使资源可用的预定义的方式,然后语言(写在程序)将提供一些实用工具/框架帮助引起公开的资源和得到结果后B的协议。

To be more specific to your question, for interpreted languages, the process is fairly universal, the protocol is normally among the lines of command line parameter, HTTP request and other ways of transmitting plain text. Take the first example, program B will receive a call from HTTP request as input, and then process the request from there on. The actual format of input is totally decided by program B.

更具体地说,对于解释语言来说,这个过程是相当普遍的,协议通常在命令行参数、HTTP请求和其他传输纯文本的方式之间。以第一个示例为例,程序B将接收来自HTTP请求的调用作为输入,然后从那里处理请求。输入的实际格式完全由程序B决定。

Things like SOAP and etc, are just a way to regulate programs to take input in a commonly agreed standard.

诸如SOAP之类的东西,只是一种管理程序的方法,以便在一个共同同意的标准中接受输入。

#7


1  

It's been a decade or so, but I did exactly this for my senior capstone (Well, I built a back-propogating neural network in C, and used a scheme program to teach it). The version of Scheme I was using had a compiler as well as an intepreter, and I was able to build it as a .o file. I don't know the version of scheme I was running, but it appears the RScheme will turn your scheme code into C.

已经有十年了,但我确实为我的高级指挥官做了这件事(嗯,我用C语言构建了一个反向的神经网络,并使用一个计划程序来教授它)。我使用的Scheme版本有一个编译器和一个intepreter,我可以把它构建成一个.o文件。我不知道我正在运行的方案的版本,但是看起来RScheme将把您的方案代码转换成C。