Преобразование структур С++ в XML и обратно
[info]maxvasin

Сохранение стуктур довольно распространённая задача, но в тоже её реализация на С++ требует трёх раздельных кусков кода:

  • определение типа структуры;
  • реализации функции(й) записи структуры в файл;
  • реализации функции(й) чтения структуры из файла.

Как следствие, изменение структур ведёт к правке кода в трёх различных местах. Использование функций read и write (и их аналогов из стандартной библиотеки C++) я не рассматриваю по причине сербёзных недостатков такого подхода:

  • получаемый файл являтся двоичным и его невозможно править обычными текстовыми редакторами;
  • формат файла зависит от компилятора и является потенциально непереносимым на другие процессоры, компиляторы и даже другие версии используемого компилятора;
  • изменение структур данных ведёт к изменению формата и для сохранения возможности чтения старых данных требуется принимать дополнительные усилия.

Указанные недостатки отстутствуют у форматов, представляющих данные в виде протого текста. Одним из таких форматов является XML. Для стуктур С++ можно определить ряд соглашений, позволяющих автоматизировать их преобразование из/в XML.

Для моих нужд достаточно преобразования простых стуктур (не содержащих указателей) в XML. Для этого (а также для того чтобы ознакомиться с XSLT) я написал программу, генерирующую по XML-описанию структуры её определение на C++ и функции чтения/записи из/в XML. Описание стуктуры выглядит следующим образом:

<module name="Settings">
  <struct name="ReferenceValue"
	  define="no"
	  serialize-as="empty-element">
    <double name="nominalValue"/>
    <double name="minusDelta"/>
    <double name="plusDelta"/>
  </struct>

  <struct name="Foo" 
	  description="A Foo" 
	  property-page="yes">
    <int name="a" description="an integer field"/>
    <double name="b" description="a double field"/>
    <double name="c" dimensions="3,2" description="some array"/>
    <int name="d" dimensions="3" description="another array"/>
    <instance name="rv" dimensions="2" type="ReferenceValue"/>
  </struct>

  <struct name="Agg" export-reader='yes' export-writer="yes">
    <instance name="foo" type="Foo"/>
  </struct>
</module>
Метки: , , ,

mavola: управление по D-Bus. Часть 3
[info]maxvasin

Добавил синтаксис определения публикуемых методов:

(define/exposed (interface-name.method-name arg1 ...) 
  :: (arg-type-1 ...) -> (result-type-1 ...) @ /object/path
  method-code ...)

Например:

(define/exposed (org.gitorious.Mavola.Unmount block.device) 
  :: (string) -> (boolean) @ /org/gitorious/Mavola
  (match (hal:Manager 'FindDeviceStringMatch "block.device" block.device)
    [(list udi)
     (with-handlers ([libdbus:exn:dbus-error? (lambda (exn) #f)])
       ((hal:Manager '#:neighbour-facet udi '()) 'Unmount '())
       (let ([dev (hal:Manager '#:neighbour-facet udi '())])
         (map (lambda (h) (h dev)) *VOLUME-POSTUNMOUNT-HOOKS*))
       #t)]
    [else
     #f]))
Метки: , ,

mavola: управление по D-Bus. Часть 2
[info]maxvasin

Доработал средства публикации методов. Теперь не требуется в ручную реализовывать для каждого объекта интерфейс org.freedesktop.Interspectable. Типы аргументов и возвращаемых значений задаются списком (in-arg-1 in-arg-2 ... in-arg-N (out-arg-1 out-arg-2 ... out-arg-N)). Например:

(expose-method '/org/gitorious/Mavola 'org.gitorious.Mavola 'Unmount
               (lambda (block.device)
                 (match (hal:Manager 'FindDeviceStringMatch "block.device" block.device)
                   [(list udi)
                    (with-handlers ([libdbus:exn:dbus-error? (lambda (exn) #f)])
                      ((hal:Manager '#:neighbour-facet udi '()) 'Unmount '())
                      (let ([dev (hal:Manager '#:neighbour-facet udi '())])
                        (map (lambda (h) (h dev)) *VOLUME-POSTUNMOUNT-HOOKS*))
                      #t)]
                   [else
                    #f]))
               '(string (boolean)))

Типы теперь представляются не строками в соответствии с правилами D-Bus, а символами (простые типы) или списками (контейнеры). Имена объекта, интерфейса и метода также представляются символами.

Метки: , ,

mavola: управление по D-Bus
[info]maxvasin
Добавил в mavola интерфейс управления по D-Bus. Привязка D-Bus к Scheme пока обеспечивает только базовое функции выставления методов, нет средств манипулирования объектами и интерфейсами, нет поддержки интроспекции (интерфейс org.freedesktop.DBus.Introspectable нужно реализовывать вручную). Выставление метода сейчас выглядит следующим образом:

(expose-method "/org/gitorious/Mavola" "org.gitorious.Mavola" "Unmount"
               (lambda (block.device)
                 (match (hal:Manager 'FindDeviceStringMatch "block.device" block.device)
                   [(list udi)
                    (with-handlers ([libdbus:exn:dbus-error? (lambda (exn) #f)])
                      ((hal:Manager '#:neighbour-facet udi '()) 'Unmount '())
                      #t)]
                   [else
                    #f]))
               '("b"))


Параметры процедуры expose-method указывают: путь объекта, имя интерфейса, имя выставляемого метода, процедура-обработчик метода и типы возвращаемых значений.

Управлять mavola из командной оболочки можно, например, следующим образом:

max@charon:~/ > dbus-send --print-reply --session --dest=org.gitorious.Mavola --type=method_call \
                          /org/gitorious/Mavola org.gitorious.Mavola.GetMountedVolumes
method return sender=:1.56 -> dest=:1.57 reply_serial=2
   array [
      string "/dev/sdb1"
      string "/dev/hda5"
      string "/dev/hda3"
   ]
max@charon:~/ > dbus-send --print-reply --session --dest=org.gitorious.Mavola --type=method_call \
                          /org/gitorious/Mavola org.gitorious.Mavola.Unmount string:/dev/sdb1
method return sender=:1.56 -> dest=:1.58 reply_serial=2
   boolean true
max@charon:~/ > dbus-send --print-reply --session --dest=org.gitorious.Mavola --type=method_call \
                          /org/gitorious/Mavola org.gitorious.Mavola.GetMountedVolumes
method return sender=:1.56 -> dest=:1.59 reply_serial=2
   array [
      string "/dev/hda5"
      string "/dev/hda3"
   ]
max@charon:~/ > dbus-send --session --dest=org.gitorious.Mavola --type=method_call \
                          /org/gitorious/Mavola org.gitorious.Mavola.Quit 
max@charon:~/ >                   
Метки: , ,

Настройка mavola. Задание точек монтирования.
[info]maxvasin
В ряде случаев требуется обеспечить монтирование тома в один и тот-же каталог, вне зависимости от того сколько переносных устройств подключено и в каком порядке. Это важно, например, для размещённого на переносном жёстком диске репозитория дистрибутива ОС. Для определения точки монтирования mavola использует список вспомогательных функций, добавление новых функций в который осуществляется функцией add-mount-point-helper!:

(add-mount-point-helper!
 (lambda (dev)
   (if (string=? (dev 'GetProperty "volume.uuid") "5c8e1c77-3a9b-4309-b89f-a0d67d8266a3")
       "mobile-disk"
       #f)))


В этом примере добавляет вспомогательная функция, которая указывает монтировать том с идентификатором 5c8e1c77-3a9b-4309-b89f-a0d67d8266a3 в каталог /media/mobile-disk. Определить идентификатор том, можно разными способами, например, запросив его у HAL. Если в настоящее время тому соответствует блочное устройство /dev/sdb1, то получить его идентификатор можно так:

> (define manager (make-facet system-bus "org.freedesktop.Hal" "/org/freedesktop/Hal/Manager"))
> (define sdb1
    (make-facet (manager '#:neighbour-object (car (manager 'FindDeviceStringMatch "block.device" "/dev/sdb1")))))
> (sdb1 'GetProperty "volume.uuid")
"5c8e1c77-3a9b-4309-b89f-a0d67d8266a3"


Специальный метод '#:neighbour-object используется для создания соседнего объекта, т.е. распооженного на той же шине и в той же службе, что и исходный объект (в этом случае /org/freedesktop/Hal/Manager).
Метки: , ,

Автомонтировщик или прикладное велосипедостроительство
[info]maxvasin
Доделал до минимально пригодного к использованию автомонтировщик mavola, ради которого и начинал привязывать dbus к PLT Scheme. Конфигурация mavola представляет собой программу на Scheme, что позволяет (в идеале) переопределить (практически) все аспекты её поведения. Например:<

(set-filesystem-mount-options! "ext2" '("noatime"))
(add-volume-postmount-hook!
 (lambda (dev)
   (with-properties (block.device) dev
                    (display (format "Device ~a mounted~%" block.device)))))


Здесь первая строка задает параметры монтирования для томов с ФС ext2 (noatime). Далее добавляется обработчик в ловушку постмонтирования (вызываемую после успешного монтирования тома). with-properties - сокращённый синтаксис доступа к свойствам грани, это выражение эквивалентно:

(let ([block.device (dev 'GetProperty "block.device")])
 ...)


mavola размещен на хостинге Gitorious, исходный код последней версии можно получить командой:

git clone git://gitorious.org/mavola/mainline.git
Метки: , ,

PLT Scheme и D-Bus
[info]maxvasin
Можно показать первые результаты по интерфейсу PLT Scheme к DBus. Сейчас есть почти полная привязка (binding) к libdbus и ряд средств абстракции для вызова методов объектов и обработки сигналов. Основной механизм абстракции вызова методов - грани, функции служащие для вызова методов из одного или нескольких интерфейсов объекта. Например, для грани, представляющей объект /org/freedesktop/Hal/Manager вызов метода org.freedesktop.Hal.Manager.GetAllDevices записывается следующим образом:
;; Определение грани
(define hal:Manager (make-facet system-bus
                                "org.freedesktop.Hal"
                                "/org/freedesktop/Hal/Manager"
                                "org.freedesktop.Hal.Manager"))

;; Вызов метода
(hal:Manager 'GetAllDevices)
Для грани, содержащей интерфейс org.freedesktop.Hal.Device.Volume монтирование тома зпишется в виде (здесь mount-point - точка монтирования, volume.fstype - тип файловой системы):
(dev 'Mount mount-point volume.fstype '(""))
Тип последнего аргумента (параметры монтирования) - "as", однако, из-за ошибки в реализации пустые массивы обрабатываются некорректно. Функция make-facet использует механизм интроспекции для получения типов аргументов методов, поэтому при передаче грани целочисленного значения оно будет преобразовано в тип соответствующего параметра. Также эта информация должна учитываться при передаче массивов.
Метки: ,

Гляделки PDF'а
[info]maxvasin
Был неприятно удивлен при попытке поставить evince (причем версию без поддержки GNOME): он все-равно хочет поставить не тольно части GNOME, но и (sic!) synaptic. Он-то ему зачем понадобился... При этом aptitude без рекомендованных пакетов захотел скачать 33М и занять 130М, и это под программу просмотра pdf, djvu и ps. То ли я чего-то не понимаю, то ли что-то не так в датском королевстве.

BibTeX
[info]maxvasin
Написание замены BibTeX'у заглохло на стадии создания стиля, в результате библиографию к диссертации генерировал bibtex8, который, надо отметить, совсем не идеален.

Эксперимент с созданием типобезопасного языка стилей провалился, надо будет откатиться до старой версии с проверкой типа записи и наличия полей при формировании библиографии. Использованный для автоматического формирования части кода Template Haskell - тихий ужас, или я может просто избалован макрами Common Lisp'а.

EOS (End Of Summer)
[info]maxvasin
Лето заканчивается, а с ним и отпуск :-(

Замена BibTeX
[info]maxvasin
Решил написать замену BibTeX'у, основная моя претензия к которому - нечеловеческий язык стилей. Для формирования библиографии в соответствии с ГОСТом можно, конечно, использовать bibtex8 с соответствующими стилями, но написание программы формирования библиографии, стили которой можно было бы писать в декларативном стиле, представляется для меня более правильным подходом.

Сейчас имеется сама программа, включающая анализатор aux-файлов, анализатор библиотек BibTeX'а, основа DSL для написания стилей и основа стиля для формирования библиографии в соответствии с ГОСТом. Программа пишется на Haskell'е, DSL стилей встроен в Haskell. Стили динамически подключаются с помощью hs-plugins (как следствие программа зависит от GHC).

Home