Выпущена серия Mac с чипом Apple Silicon M1. Вас не удивило его потрясающее исполнение? Я тоже был. Я надеялся, что мы сможем использовать замечательную мощь и экосистему Apple для программирования на Эликсире, поэтому я попытался написать код Эликсира для вызова кода Swift. В этой статье показано, как вызвать код Swift из Elixir через NIF.
Вот репозиторий такого примера кода:
24 ноября 2020 г .: Я обновил Makefile в репозитории, чтобы иметь возможность создавать его в случае инициализированного Mac, поэтому на приведенных ниже цифрах есть некоторые неправильные моменты, хотя пояснения обновлены.
Я объясню это в деталях. Вот так!
Как вызвать нативный код из кода на Эликсире
Прежде всего, я объясню, как вызвать собственный код из кода Elixir. Вы можете использовать для этого NIF. Все, что вам нужно сделать, это выполнить следующие шаги:
- Измените «mix.exs», чтобы использовать «elixir_make»;
- Напишите код C;
- Напишите «Makefile», чтобы скомпилировать его;
- Напишите код на Elixir, чтобы загрузить библиотеку нативного кода и вызвать ее
Используйте «elixir_make»
«Elixir_make» - это шестнадцатеричный модуль, который позволяет нам создавать проект с помощью команды «make».
Измените mix.exs следующим образом:
Затем запустите «mix deps.get», чтобы импортировать elixir_make.
Напишите код C
Запустите «mkdir -p c_src», создайте новый файл с именем «libnif.c» и напишите следующий временный код C в каталоге c_src:
«Erl_nif.h» - это файл заголовка NIF на Erlang. Это необходимо при использовании NIF API.
Функция test в коде C имеет три аргумента: env, argc и argv. «Env» означает среду на Erlang VM. «Argc» и «argv» образуют массив аргументов переменной «test» в коде Elixir.
Эта функция возвращает кортеж атомов {: error,: not_implemented} во времени. «Enif_make_atom» создает атом со значением, заданным его вторым аргументом, используя среду на Erlang VM, заданную его 1-м аргументом. «Enif_make_tuple» создает кортеж, содержащий некоторые элементы, используя среду, заданную его 1-м аргументом. У него есть аргументы переменной длины. Количество элементов задается вторым аргументом. Тела элементов даны 3-м и последующими аргументами.
«Nif_funcs» - это таблица функций функции NIF. Он дает информацию о том, что существует функция NIF с именем «test». Число 0 в «nif_funcs» обозначает арность функции «test», что означает, что «test» в коде Elixir не имеет аргументов.
«ERL_NIF_INIT» задает точку входа для функций NIF. В этом случае «SwiftNifTest» должен быть модулем-заглушкой Elixir, в котором все функции определены в «nif_func», который определен в каталоге «lib». В противном случае вы получите сообщение об ошибке при загрузке модуля. Вы можете использовать другой модуль, но вы должны определить его в каталоге «lib». Вы также можете описать поведение при перезагрузке в «ERL_NIF_INIT», но в этой статье оно будет опущено.
Напишите «Makefile»
Создайте новый файл с именем «Makefile» в верхнем каталоге модуля Elixir и напишите следующий код:
Я объясню его краткое содержание:
- Собственным компилятором должен быть Apple Clang, потому что он будет компилировать код Objective-C и Swift, поэтому «CC» следует определять как «xcrun clang» при компиляции на Mac.
- Переменные среды «ERL_EI_INCLUDE_DIR» и «ERL_EI_LIBDIR» будут установлены в каталог включаемых заголовков и библиотек Erlang, соответственно, в случае кросс-компиляции Nerves. В противном случае они должны быть правильно установлены этим «Makefile».
- Для переменной окружения «LDFLAGS» должна быть установлена как минимум опция «-shared». Кроме того, если не выполняется кросс-компиляция и не Windows, переменные среды «LDFLAGS» и «CFLAGS» должны иметь параметр «-fPIC». Кроме того, в случае Mac, для «LDFLAGS» должны быть установлены параметры «-dynamiclib» и «-undefined dynamic_lookup».
- Цель, которая компилируется и связывается этим «Makefile», находится в «priv / libnif.so», который указан «NIF».
- Исходные файлы указаны как «C_SRCS». Объектные файлы автоматически определяются как «C_OBJS» путем замены подстрок «C_SRCS». Они генерируются в каталоге «obj».
- Этот «Makefile» также автоматически определяет зависимости исходного кода. Это реализуется с помощью «C_DEPS», компилирующего каждый исходный код с параметрами «-MM -MP -MF» и включающими зависимости.
Напишите заглушку кода Эликсира
Измените код Elixir в каталоге «lib», чтобы загрузить библиотеку NIF и сформировать заглушку для вызова функций NIF, определенных в библиотеке:
«@On_load» с атомом заставляет модуль выполнять функцию входа, имя которой задается атомом при загрузке модуля.
«: Code.priv_dir (: swift_nif_test)» дает каталог «priv» в верхнем каталоге модуля «SwiftNifTest».
«: Erlang.load_nif» заставляет модуль загружать библиотеку NIF, расположенную по пути, указанному 1-м аргументом («nif_file»), который возвращает «: ok», когда это удалось, или «: error» по какой-то причине.
Этот модуль должен определять заглушку каждой из функций NIF. Такова «тестовая» функция. Его следует определить как код, вызывающий исключение, которое выполняется при его вызове в случае, если библиотека NIF не была загружена.
Тестовый код NIF
Запустите «iex -S mix», запустите «SwiftNifTest.test ()» в iex, и вы получите результат «{: error,: not_implemented}», который определен функцией «test» в «libnif.c». », Временно.
Как вызвать код Swift 5.3 из кода Objective-C
Невозможно вызвать код Swift из кода C напрямую. Код Swift должен вызываться из кода C через код Objective-C.
Свифт-код Callee
Пример кода Swift находится здесь:
Исходный код отсюда:
Чтобы вызвать его из Objective-C, вы добавляете «@objc» к определениям класса и функций, которые вы хотите вызвать из Objective-C.
Код вызывающего объекта Objective-C
Чтобы вызвать код Swift из кода C, вы должны определить функцию оболочки C в коде Objective-C, который вызывает код Swift, который состоит из следующих файлов заголовков и модулей в каталоге c_src:
Ожидается, что результаты, равные результатам следующего кода Swift, будут показаны при вызове функции caller:
ExampleClass-Swift.h - самый важный момент для их компиляции и связывания. Конечно, «ExampleClass» соответствует имени класса, который определен в коде Swift. Когда класс будет переименован, его следует заменить на другое имя. Компилятор Swift «swiftc» с параметром «-emit-objc-header» может сгенерировать его из кода Swift (я объясню это позже).
Модификация «Makefile»
Чтобы скомпилировать и связать их, измените «Makefile» следующим образом:
Краткое описание модификации «Makefile» выглядит следующим образом:
- Укажите путь к библиотеке, который будет включать библиотеки Swift, установив
«LDFLAGS + = -L`xcrun - show-sdk-path` / usr / lib / swift»; - Скомпилировать код Objective-C с помощью Apple Clang (xcrun clang);
- Скомпилируйте код Swift с помощью «swiftc» (xcrun swiftc) с параметрами «-emit-object -parse-as-library»;
- Создайте заголовок «ExampleClass-Swift.h» с помощью «swiftc» («xcrun swiftc») с параметрами «-emit-objc-header -emit-objc-header-path»;
- Свяжите их и код C с помощью «Apple Clang» («xcrun clang») с путем к библиотеке, указанным в 1.
Результаты
Запустите «iex -S mix», «SwiftNifTest.test ()» и получите следующие результаты:
Это все!