Virgil

топ 100 блогов love5an09.10.2010 Привет всем, кто это читает, в т.ч. в различных лентах.

Анонсирую библиотеку Virgil - продвинутый высокоуровневый FFI для CL
http://github.com/Lovesan/virgil

Virgil это ссылка на Данте, да.

Библиотека базируется на cffi, но ориентирована на маршалинг, в противовес манипуляциям сырой памятью.

В зависимостях - cffi, babel(для поддержки строк в разных кодировках) и alexandria

Среди особенностей - поддержка ссылок(references) в стиле C++

Вообще, изначально это был бэкенд в другой моей библиотеке, ldx, но потом я его полностью переписал(и ldx теперь будет основываться на нем)

Банальный пример использования:

(define-external-function
    ("write" put-string)
    ()
  (int rv (if (>= rv 0)
            rv
            nil))
  "Puts a string to the specified file descriptor(stdout by default)"
  (fd int :optional 1)
  (string (& string))
  (count int :aux (length string)))


(put-string "Hello, world!") вернет 13 и напечатает 'Hello, world!' на stdout'е
(put-string "error" 2) - 5 и 'error' на stderr соответственно, а
(put-string "123" 0) вернет NIL

По пунктам:
Имя внешней функции и имя определяемой лисповской функции:

("write" put-string)


Тут пустой список, но вообще тут два необязательных параметра - конвенции вызова и библиотека, из которой внешнее имя вытаскивается [ по дефолту = (:cdecl :default) ]:

()


Тут список из трех элементов - тип возвращаемого значения, в данном случае - int, имя возвращаемого значения(rv) и "форма возвращаемого значения", которая позволяет определять, что именно возвращается после вызова функции. В данном случае мы определем, чтобы при ошибке write(2) функция нам возвращала NIL:

(int rv (if (>= rv 0)
          rv
          nil))


Строка документации:

"Puts a string to the specified file descriptor(stdout by default)"


Дальше идут спецификации аргументов функции в форме (имя тип &optional тип-параметра начальное-значение)
Тип параметра - одно из :primary(дефолтно) :aux :key и :optional,
которые определяют соответствующий тип аргумента в лисповской функции

(fd int :optional 1)


Дальше начинается интересное.

string это встроенный тип для строк, который в полной форме выглядит как
(string :encoding имя-кодировки[по умолчанию - :ascii] :byte-length длина-в-байтах)
(если :byte-length не указана, считается, что строка нуль-терминирована),

А вот (& string) это ссылка на строку.
Фактически, это указатель, но функция принимает в аргументы строку, и внутри, перед вызовом write, выделяет память под нее, маршалит в соответствии с кодировкой, а после вызова - освобождает память. Под определенные типы(с фиксированным размером) память может выделяться на стеке, а под некоторые - совсем не выделяться(конкретно - под массивы определенных типов в некоторых реализациях CL - указатель на данные просто берется из самого массива)

Тип "&", кроме типа на который ссылается, в полной форме имеет еще два аргумента - характер передачи данных(:in :out или :inout)[про это далее] и возможность обнуления
[если последнее не nil, указатель может быть нулевой, а лисповское значение - специальной константой void]

(string (& string))


:aux параметр не появляется в аргументах лисповской функции

(count int :aux (length string))


Среди встроенных типов есть структуры, которые определяются очень похоже на defstruct:
(define-struct (float4
                (:constructor float4 (x y z w))
                (:type (vector single-float)))
  (x float)
  (y float)
  (z float)
  (w float))


Теперь самое интересное. Как я сказал, ссылки могут определять характер передачи данных(по дефолту :in)

То есть, с их помощью мы можем передавать лисповские данные в нативные функции,
а нативные функции могут эти данные изменять. И, в коллбэках - наоборот.

Вот пример.
Определяем коллбэк, который у нас будет играть роль сторонней функции:

(define-callback (float4-add :stdcall)
    void ((out (& float4 :inout)) (v (& float4)))
  (map-into out #'+ out v))


И вызываем его из лиспа:

(let ((v (float4 1.0 2.0 3.0 4.0)))
   (external-pointer-call (get-callback 'float4-add)
     ((:stdcall)
      (void rv outval)
      ((& float4 :inout) outval)
      ((& float4) inval))
     v
     v))


==> #(2.0 4.0 6.0 8.0)

Кстати, поддерживаются функции с переменным числом аргументов:

(define-external-function "printf"
    ()
  (int)
  (format (& string))
  &rest)


define-external-function с аргументом &rest в конце списка типов создает макрос, а не функцию.

Создаваемый макрос имеет сигнатуру вида
(имя ((&optional новая-форма-возвращаемого-значения)
       &rest типы-дополнительных-аргументов)
      &rest аргументы)

Пример использования:

(printf (() (uint)) "%08X" 3405691582)

==> 8
и 'CAFEBABE' на stdout'е

Библиотека только вышла, и пока очень не хватает тестов, и, особенно, документации, но это временно.

Оставить комментарий

Архив записей в блогах:
Этот проект я очень сильно ждал и возлагал на него огромные надежды. Шли дни, недели и месяцы, а вера в фильм постепенно угасала. Угасала она и после каждого отвратительного трейлера, который оставлял меня в недоумении и вызывал мысли типа: "Что ...
Блестящий плей-офф КХЛ, увы, был омрачен провальным судейством в матче номер два финала. Арбитр Гашилов в связке с Беловым повел себя в стиле "как бы чего не вышло". И, как это принято у нас, напряженных россиян-перестраховщиков, среагировал на ...
Сегодня пробил час "Хе" для избранных. Перепёлок. Ничего больше писать не буду. Просто смотрите и завидуйте, особенно те, кто мог сегодня приехать, но не приехал ;) Скажу только, что это очень вкусно. Отдельное и большое спасибо Элине Шарафетдиновой за офигенные маринованные ...
В Белоруссии шесть оппозиционных кандидатов в президенты рука об руки друг с другом и со своими сторонниками вышли на площадь и под дубинки ОМОНа. А чуть раньше в Рунете сторонники Эдуарда Лимонова и члены "Демвыбора" Владимира Милова ...
Фото © Reuters. Специально для тех, кто любят горланить «А вот во Франции…» или «А вот в Европе…». Очередной субботний московский #протест стал уже отчётливо напоминать дикий микст из лимоновской «Стратегии 31», тихо почившей в бозе, и белоленточных «Прогулок по бульварам», ...