Шрифт:
Интервал:
Закладка:
}
Привязки Crystal будут выглядеть так:
@[Link(ldflags: "#{__DIR__}/callback.o")]
lib LibCallback
fun number_callback(callback : LibC::Int -> Void) : Void
end
LibCallback.number_callback ->(value) { puts "Generated: #{value}" }
В этом примере мы передаем Proc(LibC::Int, Nil) в качестве значения аргумента обратного вызова C. Обычно вам нужно будет ввести значение аргумента Proc. Однако, поскольку мы передаем Proc напрямую, компилятор может определить его на основе типа привязанного развлечения и ввести его за нас. Тип обязателен, если мы сначала присвоили его переменной, например callback = ->(value : LibC::Int) { ... }.
Обратный вызов напечатает, какое случайное значение сгенерировал код C. Помните: прежде чем мы сможем запустить код Crystal, нам нужно скомпилировать код C в объектный файл с помощью этой команды: gcc -Wall -O3 -march=native -c callback.c -o callback.o. После этого вы можете свободно запускать код Crystal несколько раз и утверждать, что он каждый раз генерирует новое число.
Хотя мы можем передавать Procs как функцию обратного вызова, вы не можете передать замыкание, например, если вы попытались сослаться на переменную, определенную вне Proc внутри него. Например, если мы хотим умножить сгенерированное значение C на некоторый множитель:
multiplier = 5
LibCallback.number_callback ->(value : LibC::Int) { puts
value * multiplier }
Выполнение этого приведет к ошибке времени компиляции: Ошибка: невозможно отправить замыкание в функцию C (замыкающие переменные: множитель).
Передача замыкания возможна, но это немного сложнее. Я бы предложил проверить этот пример в документации Crystal API: https://crystal-lang.org/api/Proc.html#passing-a-proc-to-a-c-function. Как упоминалось ранее, привязки C могут быть отличным способом использования уже существующего кода C. Теперь, когда вы знаете, как подключаться к библиотеке, писать привязки и использовать их в Crystal, вы можете фактически использовать код библиотеки C. Далее перейдем к написанию привязок для libnotify.
Привязка libnotify
Одним из преимуществ написания привязок C в Crystal является то, что вам нужно привязывать только то, что вам нужно. Другими словами, нам не нужно полностью привязывать libnotify, если мы собираемся использовать лишь небольшую его часть. На самом деле нам нужны всего четыре функции:
• notify_init – используется для инициализации libnotify.
• notify_uninit — используется для деинициализации libnotify.
• notify_notification_new — используется для создания нового уведомления.
• notify_notification_show – используется для отображения объекта уведомления.
В дополнение к этим методам нам также необходимо определить одну структуру NotifyNotification, которая представляет собой отображаемое уведомление.
Я определил это, просмотрев файлы *.h libnotify на GitHub: https://github.com/GNOME/libnotify/blob/master/libnotify. HTML-документация Libnotify также включена в папку этой главы на GitHub, и ее можно использовать в качестве дополнительной справки.
Основываясь на информации из их документации, исходном коде и том, что мы узнали в последнем разделе, привязки, которые нам нужны для libnotify, будут выглядеть следующим образом:
@[Link("libnotify")]
lib LibNotify
alias GInt = LibC::Int
alias GBool = GInt
alias GChar = LibC::Char
type NotifyNotification = Void*
fun notify_init(app_name : LibC::Char*) : GBool
fun notify_uninit : Void
fun notify_notification_new(summary : GChar*, body :
GChar*, icon : GChar*) : NotifyNotification*
fun notify_notification_show(notification :
NotifyNotification*, error : Void**) : GBool
fun notify_notification_update(notification :
NotifyNotification*, summary : GChar*, body : Gchar*, icon : GChar*) : GBool
end
Обратите внимание: в отличие от других случаев, мы можем просто передать “libnotify” в качестве аргумента аннотации Link. Мы можем это сделать, поскольку соответствующая библиотека уже установлена в масштабе всей системы, а не является созданным нами специальным файлом.
Под капотом Crystal использует https://www.freedesktop.org/wiki/Software/pkg-config, если таковой имеется, чтобы определить, что следует передать компоновщику для правильного связывания библиотеки. Например, если бы мы проверили команду полной ссылки, которую Crystal выполняет при сборке нашего двоичного файла, мы бы смогли увидеть, какие флаги используются. Чтобы увидеть эту команду, добавьте флаг --verbose к команде сборки, которая будет выглядеть как Crystal build --verbose src/transform_cli.cr. Это выведет достаточное количество информации, но мы хотим посмотреть в самом конце, после опции -o, указывающей, каким будет имя выходного двоичного файла. Если бы мы запустили pkg-config --libs libnotify, мы бы получили -lnotify -lgdk_pixbuf-2.0 -lgio-2.0 -lgobject-2.0 -lglib-2.0, что мы также можем увидеть в команде необработанной ссылки.
Если pkg-config не установлен или недоступен, Crystal попытается передать флаг -llibnotify, который может работать или не работать в зависимости от связываемой библиотеки. В нашем случае это не так. Также можно явно указать, какие флаги следует передавать компоновщику, используя поле аннотации ldflags, которое будет иметь вид @[Link(ldflags: "...")].
Еще следует отметить, что мы используем некоторые псевдонимы в библиотеке lib. Псевдонимы в этом контексте действуют так же, как стандартные псевдонимы Crystal. Причина, по которой мы их определили, состоит в том, чтобы сделать код немного проще в сопровождении, оставаясь как можно ближе к фактическому определению методов. Если в будущем создатели библиотеки захотят изменить значение GInt, мы также легко сможем это поддержать.
Для представления типа уведомления мы используем ключевое слово type для создания непрозрачного типа, поддерживаемого указателем void, что нам может сойти с рук, поскольку нам не нужно фактически ссылаться или взаимодействовать с фактическим внутренним представлением уведомления в libnotify. Это также служит хорошим примером того, что не все нужно связывать, особенно если оно не будет использоваться.
Причина создания NotifyNotification непрозрачного типа заключается в том, что libnotify обрабатывает создание/обновление структуры внутри себя. Ключевое слово type позволяет нам создавать что-то, на что мы можем ссылаться в нашем коде Crystal, не заботясь о том, как это было создано.
В случае notify_notification_show мы сделали второй аргумент типа Void, поскольку предполагаем, что все работает так, как ожидалось. Мы также связали функцию notify_notification_update. Этот метод на самом деле не обязателен, но он поможет кое-что продемонстрировать позже в этом разделе, так что следите за обновлениями!
Тестирование привязок
Следующий вопрос, на который нам нужно ответить: куда нам следует поместить файл привязки? Идеальным решением было бы создать выделенный сегмент и потребовать его в качестве зависимости. Основное преимущество, которое это дает, заключается в том, что другие могут использовать их независимо от источника нашего приложения CLI. Однако для целей этой демонстрации мы просто добавим их в исходные файлы нашего приложения CLI.