Как код, написанный на одном языке, вызывается с другого языка

Это вопрос, который я всегда хотел узнать, но никогда не спрашивал.

Как код, написанный на одном языке, особенно интерпретируемый язык, вызывается кодом, написанным скомпилированным языком.

Например, скажем, я пишу игру на C ++, и я передаю часть поведения AI, которое должно быть написано на Схеме. Как код, написанный на Схеме, попадает в точку, которая может использоваться скомпилированным кодом на C ++? Как он используется исходным кодом C ++ и как он используется скомпилированным кодом C ++? Есть ли разница в том, как он используется?

связанные с

Как взаимодействовать несколько языков в одном проекте?

Нет единого ответа на вопрос, который работает повсюду. В общем, ответ заключается в том, что два языка должны согласовать «что-то» – набор или правила или «протокол вызова».

На высоком уровне любой протокол должен указывать три вещи:

  • «открытие»: как найти друг друга.
  • «linking»: как установить соединение (после того, как они знают друг о друге).
  • «Invocation»: как делать запросы друг к другу.

Детали в значительной степени зависят от самого протокола.

Иногда два языка сговариваются работать вместе. Иногда оба языка соглашаются поддерживать некоторый внешний протокол. В наши дни часто задействуются ОС или «среда выполнения» (.NET и Java). Иногда способность идет только в одну сторону («A» может вызывать «B», но «B» не может называть «A»).

Обратите внимание, что это та же проблема, с которой сталкивается любой язык при общении с ОС. Ядро Linux не написано на Схеме, вы знаете!

Давайте посмотрим некоторые типичные ответы из мира Windows:

  • C с C ++ : C ++ использует искаженное («искаженное») изменение «C-протокола». C ++ может вызывать на C, а C может вызывать на C ++ (хотя имена иногда могут быть довольно грязными, и может потребоваться внешняя помощь, переводящая имена). Это не только Windows; это вообще верно на всех платформах, которые поддерживают оба. Самые популярные ОС используют протокол «C».

  • VB6 и большинство языков : предпочтительным методом VB6 является «COM-протокол». Другие языки должны иметь возможность записывать COM-объекты, которые могут использоваться с VB6. VB6 также может создавать COM-объекты (хотя и не все возможные варианты COM-объектов).

    VB6 также может говорить очень ограниченный вариант «протокола C», а затем только для того, чтобы делать внешние вызовы: он не может создавать объекты, с которыми можно напрямую разговаривать через «протокол C».

  • Языки .NET : все языки .NET обмениваются данными с одним и тем же языком низкого уровня (IL). Время выполнения управляет связью, и с этой точки зрения все они выглядят одинаково.

  • VBScript и другие языки : VBScript может говорить только подмножество протокола COM.

Еще одно примечание: SOAP «Веб-службы» действительно является «протоколом вызова», как и многие другие веб-протоколы, которые становятся популярными. В конце концов, все дело в том, чтобы говорить с кодом, написанным на другом языке (и работает в другом окне)!

Обычно код C ++ вызывает интерпретатор для языка сценариев. Степень взаимодействия между скомпилированным и скриптовым кодом зависит от интерпретатора, но всегда есть способ передать данные между ними. В зависимости от интерпретатора может быть возможным манипулировать объектами с одной стороны с другой стороны, например, с помощью функции C ++, вызывающей метод для объекта Ruby. Возможно, даже есть способ контролировать выполнение одного из другого.

Существует протокол для взаимодействия модhive. Вот высокоуровневый общий обзор того, как он работает:

  1. Библиотека создана для кода, который вы хотите «поделиться». Они обычно называются DLL или SO в зависимости от вашей платформы.
  2. Каждая функция, которую вы хотите открыть (точка входа), будет доступна внешнему миру для привязки. Существуют протоколы о том, как связывать такие, как вызывающее соглашение, которое определяет порядок передачи параметров, который очищает стек, сколько параметров хранится в регистрах, а какие и т. Д. См. Cdecl, stdcall и т. Д. Для примеров вызова соглашения здесь .
  3. Вызывающий модуль будет либо статически, либо динамически привязываться к общей библиотеке.
  4. Как только ваша вызывающая библиотека привязана к общей библиотеке, она может указать, что она хочет привязываться к определенной точке входа. Обычно это делается по имени, однако большинство платформ также предлагают возможность привязки по индексу (быстрее, но более хрупко, если ваш модуль изменяется, а точки входа переупорядочиваются).
  5. Вы также обычно объявляете функцию, которую хотите вызвать в своем модуле где-нибудь, чтобы ваш язык мог выполнять проверку статического типа, знает, что такое соглашение о вызове и т. Д.

Для вашего сценария вызова схемы из C ++ интерпретатор схемы скорее всего экспортирует функцию, которая динамически связывается с функцией / объектом Scheme и вызывает это. Если модуль Scheme скомпилирован, он, вероятно, имеет возможность экспортировать точку входа, чтобы ваш C ++-модуль мог привязываться к этому. Я не очень хорошо знаком с Схемой, поэтому кто-то другой, вероятно, может ответить на специфику этого конкретного связывания лучше, чем я.

Вы также можете интегрировать две среды без необходимости компилировать библиотеку интерпретатора внутри исполняемого файла. Вы сохраняете свой exe и Scheme exe как отдельные программы в вашей системе. С вашего основного exe вы можете написать свой код схемы в файл, а затем использовать system () или exec () для запуска интерпретатора схемы. Затем вы разбираете вывод интерпретатора схемы.

Подход, предложенный выше, держит exes отдельно, и вам не нужно беспокоиться о зависимостях третьих сторон, они могут быть значительными. Также проблемы остаются в одном exe или другом.

Если запуск отдельного exe не удовлетворяет вашим требованиям к производительности, вы можете разработать протокол, в котором интерпретатор Scheme станет сервером. Вам нужно написать некоторые функции Scheme, которые ждут ввода в сокете или файле, eval, который затем выводит результат в тот же сокет или другой файл. Еще одна итерация заключается в том, чтобы посмотреть на существующие серверы, на которых уже запущен ваш интерпретатор, например, у apache есть модули, которые позволяют писать код на многих языках.

Если вы на самом деле ищете инструменты для выполнения такой задачи, ответ ля Адама, см. Swig .

С теоретической точки зрения, когда программе A необходимо использовать ресурсы (class / функции / etc) из программы B, речь идет о передаче некоторой информации от A до B и получении некоторой информации или каких-либо действий. Таким образом, должен существовать способ, предоставляемый B, который позволяет A передавать информацию и получать результат.

На практике это обычно лежит на плечах языков для обработки этого процесса: язык B (программа B записывается в) будет генерировать протокол и делать ресурсы в B доступными в предопределенном виде, тогда язык A (программа A записывается в ) предоставит некоторую утилиту / инфраструктуру, чтобы помочь выявить открытые ресурсы и получить результаты по протоколу B.

Чтобы быть более конкретным для вашего вопроса, для интерпретируемых языков процесс довольно универсален, протокол обычно входит в число параметров командной строки, HTTP-запрос и другие способы передачи обычного текста. Возьмите первый пример, программа B получит запрос от HTTP-запроса в качестве входных данных, а затем обработает запрос оттуда. Фактический формат ввода полностью определяется программой B.

Такие вещи, как SOAP и т. Д., – это всего лишь способ регулирования программ для ввода информации в общепринятый стандарт.

Прошло уже около десяти лет, но я сделал именно это для своего старшего ключевого камня (ну, я построил back-propogating нейронную сеть на C и использовал схему программы, чтобы научить ее). Версия используемой мной схемы имела компилятор, а также встроенный редактор, и я смог создать его как .o-файл. Я не знаю версию схемы, которую я запускал, но, похоже, RScheme превратит ваш код схемы в C.