|
|
Чт, 5 Ноя, 2009, 07:32 FK в sqlite
Наконец-то это произошло: в sqlite-3.6.19 появилась поддержка внешних ключей. А ведь это был самый крупный недостаток sqlite'а.
А в sqlite-3.6.8 появились вложенные транзакции, что тоже полезно в некоторых случаях. Вс, 1 Ноя, 2009, 12:32 cl-gtk2-0.1
На прошлой неделе зарелизил версию 0.1 биндинга к Gtk+ - cl-gtk2.
Исходники можно скачать с сайта проекта (http://common-lisp.net/project/cl-gtk2/files/cl-gtk2-0.1.tar.gz). Для пользователей Gentoo Linux все проще - я завел оверлей для cl-gtk2, в котором есть ebuild для cl-gtk2. Добавить оверлей можно через layman:
layman -a lisp
layman --overlays=http://github.com/dmitryvk/cl-gtk2-overlay/raw/master/layman-list.xml -a cl-gtk2
(оверлей cl-gtk2-overlay требует наличия lisp-overlay)
В 0.1 частично поддерживается clisp - исправлен еще ряд ошибок в использовании CLOS MOP, которые проявлялись в clisp. К сожалению, не удалось проверить работу под всеми ОС. В 64-битном gentoo linux в clisp не работают callback'и. По сути, работа в clisp проверялось только в 32-битной windows - там работает.
Добавлены и улучшены биндинги: GtkTextView, GtkTreePath и GtkTreeRowReference, GtkWidget, GtkContainer child properties, GtkFileChooser, GtkHSV, сериализация GtkTextBuffer, GtkTreeModelSort, GtkTreeStore, GtkListStore.
Добавлен таймер (gtk:timer), периодически вызывающий заданную функцию.
Добавил в cl-gtk2 поддержку Cairo. Реализовал самый простой способ - интеграцию с cl-cairo2.
Основной вариант использования Cairo - это отрисовка виджетов. Для этого в обработчике события expose-event создается контекст Cairo, и на нем производится собственно рисование.
Для создания контекста используется макрос cl-gtk2-cairo:with-gdk-context, который создает и освобождает контекст Cairo. Размеры виджета получаются с помощью функции gdk:drawable-get-size, которая возвращает ширину и высоту в виде двух значений.
В итоге, обработчик expose-event'а выглядит примерно следующим образом:
(lambda (widget event)
(declare (ignore event))
(multiple-value-bind (w h) (gdk:drawable-get-size (widget-window widget))
(with-gdk-context (ctx (widget-window widget))
(with-context (ctx)
(rectangle 0 0 w h)
(set-source-rgb 1 1 1)
(fill-path)
nil))))
А вот скрин из демки cl-gtk2-cairo-demo:demo:
Сегодня удалось запустить cl-gtk2 под windows.
Простая проблема была в том, что неправильно были описаны gtk'шные библиотеки (неправильные имена файлов).
Чуть более сложная проблема была в том, что CFFI оптимизировал вызов foreign-funcall с константным аргументом. А виндовый порт SBCL пытался прилинковать вызываемую функцию при компиляции даже в том случае, если вызов никогда не происходил. До оптимизации (foreign-funcall-pointer (foreign-symbol-pointer ...) ...) авторы CFFI не дошли, что хорошо:)
Текущая версия cl-gtk2 работает только с достаточно свежей версией Gtk+-2.16. Взять ее можно здесь. После распаковки библиотек следует добавить путь к gtk/bin в PATH, чтобы CFFI нашел библиотеки. Либо же можно добавить путь к gtk/bin в CFFI:*FOREIGN-LIBRARY-DIRECTORIES*. Для работы GtkGlExt аналогично следует установить GtkGlExt, GLUT (раз и два)
На удивление, все заработало почти сразу же - включая даже GtkGlExt - glut'овский чайник крутился на демке.
Но по не выясненным пока причинам, если запускать cl-gtk2 из Slime, то вызов gtk-main приводит к зависанию SBCL. Похоже, что swank делает что-то с файлами/select'ом/сигналами, что плохо взаимодействует с gtk'шным циклом обработки сообщений.
Вт, 8 Сент, 2009, 09:35 GtkParasite
Случайно наткнулся на отладчик для GUI на Gtk+ - parasite. Он работает одновременно с отлаживаемой программой и показывает иерархию виджетов, позволяет изменять свойства виджетов. В качестве мега-фичи содержит внутри себя Python Shell ( скриншот), с помощью которого можно писать всякий временный код для отладки - практичеси REPL. Очевидно, что в лиспе можно сделать лучше :)
В продолжение предыдущего поста. Переписал управление памятью в cl-gtk2. Решил несколько проблем:
- Денатурация объектов. Когда мы передаем ссылку на объект типа custom-window (о котором GObject/Gtk+ ничего не знает), то ожидаем, что обратно вернется объект типа custom-window со всеми его полями, а не объект типа gtk-window. Преобразование объекта к базовому типу и есть денатурация.
- Не всегда верный подсчет ссылок. В некоторых случаях, к объектам добавлялись лишние ссылки, что приводило к тому, что не все объекты были собраны сборщиком.
- Создание объектов классов, реализованных в Gtk+ и объектов классов-наследников GObject, определенных в лиспе, происходило немного по-разному.
- Некоторые функции Gtk+ возвращают объекты с уже добавленной ссылкой (увеличенным на единицу счетчиком ссылок). Например, gdk_gc_new и gtk_widget_create_pango_layout. Это не учитывалось, что создавало утечки.
Остался один непонятный момент, связанный с использованием GtkBuilder'а. По неясным пока причинам, на объекты, созданные GtkBuilder'ом остаются лишние ссылки. Но это уже предмет дальнейшего разбирательства.
Garbage collection is hard - я это только сейчас понял "на своей шкуре". Переделываю управление памятью (исправляю баги и хочу сделать немного более прозрачным) в cl-gtk2-gobject и натыкаюсь на разного рода неприятности. GObject использует подсчет ссылок для управления памятью. При этом, имеется 3 типа ссылок: обычные (strong references), слабые (weak references) и переключаемые (toggle references). С первыми двумя все более-менее понятно, toggle references - зверь более интересный. Основное, для чего он нужен - интеграция подсчета ссылок в gobject со сборщиками мусора других языков (биндингов). Переключаемые ссылки различают два состояния: на объект есть только toggle reference и на объект есть другие ссылки. В случае, когда на объект нет других ссылок, то это значит, что на объект есть только ссылка из биндинга, и значит, биндинг может быть уверен в том, что объект больше не "воскреснет". Если бы таких ссылок не было, биндинг не мог бы быть уверен в том, что он удаляет последнюю ссылку на объект - потом эта ссылка может "всплыть", и биндингу придется воссоздать объект, которого уже нет, и вместе с которым ушло его состояние (например, есть объект класса custom-window, отнаследованный от gtk-window; если биндинг удалит этот объект, и на него придет ссылка, то все, что сможет сделать биндинг - это вернуть ссылку как объект типа gtk-window, а это плохо; это называется денатурацией объектов). В итоге, такие ссылки позволяют биндингу держать "правильный" объект доступным на тот случай, если он понадобится. А когда "чужих" ссылок на объект нет, об объекте можно забыть (убрав сильную ссылку из хэш-таблицы указателей и оставив слабую ссылку). С такими ссылками разобраться несложно: имеем две хэш-таблицы, одну для сильных ссылок, другую - для слабых. При поиске объекта по указателю ищем в обеих таблицах, при "переключении" переключаемой ссылки - перемещаем указатель из одной хэш-таблицы в другую. Заодно нашел баг в конструировании объектов, иногда оставались лишние ссылки на объекты, а использование переключаемых ссылок окончательно решило проблему денатурации объектов (ранее эта проблема была решена только для классов, зарегистрированных в gobject). Другая найденная неприятность более неприятна. Для хранения ссылок на функции-обработчики сигналов используются stable pointers - целочисленные индексы в массив объектов. Проблема в том, что ссылки в этом массиве всегда сильные. И если обработчик сигнала ссылается на объект, чей сигнал он обрабатывает, то объект никогда не будет собран, т.к. на него есть сильная ссылка со стороны обработчика сигнала, на который есть сильная ссылка со стороны массива stable pointer'ов. Выглядит это вот так:  Решение этой проблемы состоит в том, что при переключении переключаемой ссылки в "слабое" состояние делать ссылки на соответствующие объекту обработчики также слабыми. В этом случае на объект не будет сильных ссылок со стороны обработчиков сигналов, и он будет подобран сборщиком (как и обработчики его сигналов). Осталось только реализовать это. Либо же обработчики сигналов хранить в самом объекте:
Что-то repo.or.cz недоступен. Соответственно, и git-репозитории для cl-sqlite и cl-gtk2 - тоже. На всякий случай, сделал их клоны на github:
- git://github.com/dmitryvk/cl-gtk2.git (gitweb)
- git://github.com/dmitryvk/cl-sqlite.git (gitweb)
Сб, 29 Авг, 2009, 19:21 gtk-glext
13_49 прислал мне код, добавляющий поддержку GtkGLExt в cl-gtk2. После некоторых изменений, я закоммитил изменения в git.
GtkGLExt позволяет снабдить некоторые виджеты OpenGL-контекстом, и затем рисовать на этих контекстах, используя вызовы OpenGL. В лиспе уже есть cl-opengl - биндинг к OpenGL. Посредством него в cl-gtk2-gtk-glext делаются вызовы функций OpenGL.
Создадим простую программу с GUI, которую можно будет запускать как отдельное приложение.
Программа не будет делать ничего полезного, и будет просто показывать окно с надписью “Hello, world”.
Для построения GUI воспользуемся библиотекой cl-gtk2.
Опишем процесс по шагам
Запускаем Slime, загружаем cl-gtk2:
(asdf:oos 'asdf:load-op :cl-gtk2-gtk)
Создаем исходный файл (назовем его hello-world.lisp) со следующим текстом:
(defpackage :hello-world
(:use :cl :gobject :gtk)
(:export :main :run))
(in-package :hello-world)
(defun main ()
(within-main-loop
(let ((w (make-instance 'gtk-window :title "Hello, world"))
(l (make-instance 'label :label "Hello, world!")))
(container-add w l)
(connect-signal w "destroy" (lambda (w)
(declare (ignore w))
(gtk-main-quit)))
(widget-show w))))
(defun run ()
(main)
(join-main-thread))
В этом исходнике определяется пакет hello-world, в котором определяются две функции: main и run. Функция main нужна во время разработки – она запускает программу в другой нитке (за это отвечает макрос within-main-loop), а run используется при запуске программы отдельно, не из slime.
Функция join-main-thread ожидает, когда главный цикл обработки сообщений в Gtk+ будет завершен. Этот цикл завершается, когда окно закрывается и получает сигнал “destroy”.
Можно протестировать программу прямо из Slime:
(hello-world:main)
После ввода этой формы в REPL будет запущена фоновая нить (thread), в которой работает Gtk+, и управление сразу вернется в REPL.
Теперь перейдем к созданию программы, которая может запускаться отдельно.
Для начала, следует определить ASDF-систему для нашего hello-world'а.
Создаем файл hello-world.asd в одном каталоге с hello-world.lisp следующего содержания (хотя названия системы и пакета в данном примере совпадают, это совсем не обязательно):
(defsystem :hello-world
:name "hello-world"
:components ((:file "hello-world"))
:depends-on (:cl-gtk2-gtk))
В этом описании системы указывается, как следует собирать программу: программа зависит от системы cl-gtk2-gtk и состоит из одного исходного файла hello-world.lisp (расширение .lisp добавляется автоматически).
Обычно с помощью ASDF определяются системы, содержащие код библиотек и помещаются в общесистемный каталог /usr/share/common-lisp/systems. В данном случае, определяется система для отдельной программы.
С помощью cl-launch можно превратить описание этой системы в запускающий бинарник.
Сперва надо установить cl-launch. В gentoo linux с подключенным lisp-overlay это делается вводом команды
emerge dev-lisp/cl-launch
В других дистрибутивах можно воспользоваться ASDF-INSTALL для установки в общесистемный каталог или каталог пользователя:
(require :asdf-install)
(asdf-install:install :cl-launch)
cl-launch содержит шелл-скрипт cl-launch.sh, который используется для приготовления лисповских программ к запуску как отдельные приложения.
Сперва создадим образ лиспа, в котором содержатся cl-gtk2-gtk и наш hello-world. Если не создавать образ, то придется загружать cl-gtk2-gtk из исходников или fasl'ов. Загрузка и компиляция из исходников – очень долгий процесс; загрузка из fasl'ов происходит быстрее, но все равно долго (на моем компьютере загрузка из fasl'ов занимает 30 секунд). Для разработки программ создавать образ не нужно, так как все загружается один раз при запуске.
Для того, чтобы создать образ, введем команду (находясь в одном каталоге с hello-world.lisp и hello-world.asd):
cl-launch.sh -s hello-world -d hello-world-image
cl-launch загружает систему hello-world вместе со всеми зависимости (cl-launch добавляет текущий каталог в список каталогов, в которых ASDF ищет системы, поэтому никаких симлинков на hello-world.asd создавать не нужно) и сохраняет образ в файл hello-world-image.
Если установлено несколько реализаций лиспа, то можно выбрать, какую из них использовать:
cl-launch.sh --lisp sbcl -s hello-world -d hello-world-image
Далее надо с помощью cl-launch создать шелл-скрипт, запускающий программу из созданного образа:
cl-launch.sh -m hello-world-image -i '(hello-world:run)' -o hello-world
(если установлено несколько реализаций лиспа, то также можно добавить ключ --lisp)
В результате этой команды будет создан скрипт hello-world, который запускает программу.
Попробуем запустить его:
./hello-world
Должно практически сразу же появиться окошко:
Время запуска программы из образа можно назвать вполне приемлемым: окошко появляется спустя едва различимый интервал времени.
Но у рассмотренного способа создания приложений есть один недостаток – размер получаемого образа. В моем случае (для SBCL на 64-битной машине) размер составил 64 мегабайта. Основная часть этого образа – сам SBCL (размер основного образа SBCL на моей машине – 42 мегабайта), остальная часть – это cl-gtk2-gtk и совсем небольшая часть – собственно приложение. То есть, при усложнении собственной программы и добавлении к ней кода размер образа не будет сильно расти.
Чтобы исправить эту проблему, можно сжать исполняемый образ с помощью программы gzexe.
gzexe hello-world-image
На моей машине размер образа уменьшается с 64Mb до 12Mb и несколько увеличивается время загрузки программы (с 0.5 с до 1.4 с). 12Mb – это уже более приемлемо даже для загрузки программы из интернета, но все равно много. Разные реализации лиспа создают образы по-разному, и размеры варьируются от реализации к реализации, поэтому из приведенных чисел не следует, что приложения всегда будут получаться большими.
Одним из приемлемых способов распространения приложений на лиспе для дистрибутивов линукса, по-видимому, является сборка образа из исходников на конечной машине. При таком способе общий объем загружаемых из интернета данных получается не настолько большим (при условии, что в составе дистрибутива имеется компилятор лиспа), и время запуска приложения приемлемое. При этом все используемые лисповые библиотеки можно как брать с машины пользователя (для gentoo- или debian-подобных дистрибутивов), так и держать рядом с исходниками (если нет пакетного менеджера, в репозитории которого имеется необходимый софт)
Замечание: сохранение образов с загруженным cl-gtk2 пока работает лишь в SBCL. Работоспособность сохраненных образов с cl-gtk2 в других лиспах - вопрос времени.
Сб, 8 Авг, 2009, 22:47
Переписал использование GBoxed-структур в cl-gtk2-gobject. Теперь делать биндинги к библиотекам, использующим GObject, будет интереснее.
Что дает новый type mapping для GBoxed:
- Типобезопасность. Лисповый код огражден от лишних манипуляций с указателями, ручным или полу-автоматическим управлением памятью и временем жизни объектов (речь идет про GBoxed; для GObject это было изначально)
- Более прозрачная реализация коллбэков и методов для реализуемых GObject-классов, которые в качестве аргументов или возвращаемых значений используют GBoxed
- Прозрачное использование структур в определениях foreign-функций
- GBoxed-структурами пользоваться стало удобнее
Код стал чище и, как кажется, более корректным.
Теперь можно заниматься более детальными частями биндинга. А именно, до конца дописать биндинг к Gtk+, Gdk и др.
Сб, 25 Июл, 2009, 23:28
В CFFI хорошо сделанный мануал. Сейчас из него взял подсветку синтаксиса. В исходниках CFFI есть скрипт colorize-lisp-examples.lisp, которых в html-файлах, полученных из texinfo. Мануал выглядит гораздо опрятнее с раскрашенным кодом. CFFI вообще выглядит "образцовым" проектом, в котором код достаточно качественный, документация хорошая, абстракции хорошие.
Добавил лисповскую реализацию GtkTreeModel для деревьев в cl-gtk2. А это, в свою очередь, позволяет пользоваться виджетом GtkTreeView для отображения древовидно-табличных данных. Поддержку Gtk'шного GtkTreeModel я не сделал и не вижу в этом необходимости, раз есть своя реализация GtkTreeModel.
На данном скрине запечатлено дерево разбора для лисповского выражения, задающего функцию вычисления квадратного корня (набранная по памяти из SICP). Демка вызывается выражением (gtk-demo:demo-treeview-tree).
Как можно заметить, kde я больше не использую, перешел на gnome.
В качестве средства для создания документации пока остановился на texinfo. Ни одно из средств (среди тех, которые я смотрел и которые удалось запустить) для генерации документации из исходников и documentation strings меня не устроило. Шаблон для документации взял из исходников CFFI. На основе этого шаблона уже задокументировал биндинг к GObject.
Починил в CL-GTK2 возможность сохранения образов в SBCL (всего лишь надо при сохранении образа завершать главный цикл обработки сообщений, а при восстановлении из образа инициализировать Gtk+, зарегистрировать типы, реализованные в лиспе (пока это лишь LispArrayListStore)).
С помощью cl-launch или используя sb-ext:save-lisp-and-die можно создать образ с приложением, который запускается гораздо быстрее (по сравнению с загрузкой CL-GTK2 из fasl'ов или с загрузкой из исходников).
Пт, 3 Июл, 2009, 15:50
из yosefk:
«Python is a programming language, and C++ is a karmic punishment»
В стёрки для металлических маркерных досок вставляются магнитики, чтобы оные стёрки крепились к доске. Во время перевозки такой доски положил такую стёрку в рюкзак, а через 5 минут вспомнил, что там же лежит внешний винчестер для бэкапов. Пара бэдов появилась, что неприятно. Пришлось бэкап заново делать.
Похоже, на ноутбуке в очередной (уже третий) раз ломается южный мост: не работает один из трех usb-портов. От acer'а надо избавляться при первой возможности.
В KDE, по сравнению с гномом, не нравится, что все как-то слишком сильно связано. Например, программу из KDE3 в KDE4 уже особо не запустишь. Гном как-то стройнее устроен. Базовые библиотеки, например, вообще гномонезависимы (тот же GLib уже в Qt используют). Нравится, когда нет лишних зависимостей.
Для редактирования LATEXовских файлов перешел с Kile на emacs (auctex).
KDE нравится все меньше и меньше. Программы падают, несовместимы между собой, процветает велосипедостроительство. Kopete сменил на pidgin, потом на gajim; akregator сменил на liferea; вместо KNetworkManager используется nm-applet.
Пожалуй, единственное, что держит в KDE — это konqueror, kmail и okular — одни из немногих качественных компонентов. Хотя и konqueror как браузер хочется сменить на что-нибудь более иное. А еще нравится konsole.
Реанимировал КПК, опять начал читать книжки (включая художественную литературу).
Написал полезную для себя вещь (на лиспе с использованием cl-gtk2 и cl-sqlite): список запланированных дел с оценкой времени и подсчетом разных статистик.
Медленно, неспеша начал искать новую работу.
Закончил университет (с дипломом с отличием), все оказалось и проще, и сложнее, чем казалось вначале.
Думаю, было бы полезно выложить конспекты лекции, которые у нас читались, тем более, что многие курсы были великолепными (хотя были и курсы, по которым лекции и практика были просто ужасными; самое обидное в том, что это как раз профильные для ВМК курсы: программирование и алгоритмические языки, языки программирования и методы трансляции). По неосторожности и из-за отсутствия бэкапов потеряны лекции за 1 курс (а там очень полезные вещи в части матанализа и алгебры) и частично за 3й курс. Еще часть курсов просто не набирал.
Итак, сами конспекты лекций (в алфавитном порядке):
- Анализ сложных систем
- Безопасность информации
- Дискретная математика
- Дифференциальные уравнения
- Концепции современнего естествознания
- Математическая логика
- Математические методы и модели в экономике
- Математический анализ (2 курс, 1 семестр)
- Математический анализ (2 курс, 2 семестр)
- Методы оптимизации
- Основы экономической теории
- Проектирование прикладных программных систем
- Системный анализ и автоматизация управления
- Социология
- Теория игр
- Уравнения математической физики
- Физика
- Философия
- Цифровая обработка сигналов
- Численные методы
- Экспертные системы
Для практически всех из них остались теховские исходники, если понадобятся — можно обращаться.
|