(no subject)
Dec. 8th, 2008 12:38 amВ GTK+/Glib/GObject, оказывается, продумали создание биндингов. Например, везде, где используются сигналы (способ оповещения о событиях в UI, например, сигнал "clicked" у кнопки, на который можно навешать обработчики), можно передавать не только указатель на сишную функцию, но и объект GClosure. Тут-то и заключено удобство. Во-первых, в GClosure можно передавать свои данные. Во-вторых, каждому GClosure можно сопоставить свою функцию маршалинга данных и вызова внешней функции (которая соберет аргументы, которые заботливо сложены в массив GValue, в которых указаны тип и значение), вызовет нужную функцию, и запишет ее результат в другое GValue). В-третьих, когда GClosure становится ненужным (используется подсчет ссылок), у него вызывается функция финализации (которую можно написать свою). В итоге, например, для возможности передавать лисповские замыкания в качестве обработчиков сигналов в GTK+, достаточно всего двух callback'ов: для функции маршалинга и для функции финализации.
При этом, мы имеем:
- Для создания callback'ов достаточно CFFI (а он не позволяет переносимо создавать сишные колбэки из замыканий, а только из свободных функций).
- Можем передавать замыкания в качестве обработчиков сигналов.
- Замыкания уничтожаются, когда соответствующий виджет уничтожается.
Вот что можно делать:
(g-signal-connect-closure button "clicked" (bare-gtk::create-closure (let ((count 0)) (lambda (widget) (format t "Нажал ~A раз~%" (incf count))))) +false+)
Смотря на исходники lgtk и clg (достаточно качественные биндинги), задаюсь вопросом: а зачем я это делаю? Наверное, просто так, чтобы уметь. Ну и то, что они используют sbcl/cmucl/clisp-специфичные функции, а я стараюсь оставаться в рамках CFFI (для переносимости). Особенно интересно, как в lgtk реализованы callback'и, восстановление после ошибок и сборка мусора. Это действительно стоит внимания.