Page 12 of 20

Вопросы по использованию FFI

Posted: Tue 21 May, 2013 20:20
by John Doe
FFI — одна из стандартных библиотек luajit.
Позволяет из Lua кода вызывать внешние C-функции и использовать структуры данных C.
В данной теме задаём вопросы по использованию FFI

Заготовка для документации: viewtopic.php?f=57&t=8418&p=147610#p147610

Примеры готовых скриптов:

Предупреждение: viewtopic.php?p=147610#p147610

Получение привилегий backup/restore

Posted: Fri 08 Sep, 2017 20:59
by John Doe

Latest post of the previous page:

Shmuel wrote: Fri 08 Sep, 2017 20:42К сожалению, этого одного нуля недостаточно для стрингов в UTF-16. В своё время я предложил в форуме Lua, чтобы резервировалось место для двух нулей (\0\0), но предложение принято не было.
Совсем памяти нет, уже ведь обсуждали http://forum.farmanager.com/viewtopic.p ... 56#p111556, http://forum.farmanager.com/viewtopic.p ... 01#p115201

Получение привилегий backup/restore

Posted: Fri 08 Sep, 2017 21:01
by John Doe
Shmuel wrote: Fri 08 Sep, 2017 20:57Этот уже существующий \0 не является частью стринга - он лежит в его внутреннем представлении. Поэтому, если добавить в Utf8ToUtf16 к результату \0, мы нарушим корректность преобразования.
По-моему вопрос о другом.

Code: Select all

Utf8ToUtf16(str.."\0")
Мне тоже казалось, что результат должен содержать в итоге два завершающих нуля, но сейчас усомнился (может получиться и ни одного, в зависимости от реализации).

Получение привилегий backup/restore

Posted: Fri 08 Sep, 2017 21:06
by Shmuel
John Doe wrote: Fri 08 Sep, 2017 21:01Мне тоже казалось, что результат должен содержать в итоге два завершающих нуля
Вам правильно казалось. Результат будет содержать \0\0 в конце (и ещё один \0 внутренней репрезентации).

Получение привилегий backup/restore

Posted: Fri 08 Sep, 2017 21:36
by Smitis
Shmuel wrote: Fri 08 Sep, 2017 20:57Этот уже существующий \0 не является частью стринга - он лежит в его внутреннем представлении.
Кажется понял. Ноль есть в памяти, но он не считается частью строки, поэтому строковые функции, в том числе Utf8ToUtf16, его игнорируют. Что бы получить ноль в UTF-16 его надо ЯВНО добавить в строку. Можно добавить 0 в win.Utf8ToUtf16(str.."\0"), тогда в конце буфера будет ТРИ нулевых байта или win.Utf8ToUtf16(str).."\0", тогда будет ДВА нулевых байта, что в любом случае для работы юникодных функций достаточно.

Вопросы по использованию FFI

Posted: Sat 09 Sep, 2017 09:23
by DrKnS
Shmuel wrote: Fri 08 Sep, 2017 20:57Этот уже существующий \0 не является частью стринга - он лежит в его внутреннем представлении. Поэтому, если добавить в Utf8ToUtf16 к результату \0, мы нарушим корректность преобразования.
Так вроде бы \0 уже добавляется к новому внутреннему представлению?

Вопросы по использованию FFI

Posted: Sat 09 Sep, 2017 10:37
by Shmuel
DrKnS wrote: Sat 09 Sep, 2017 09:23Так вроде бы \0 уже добавляется к новому внутреннему представлению?
Это сделано для удобства внутренних операций LuaFAR. Там же двумя строчками ниже выставляется выходной размер, и он используется при формировании строки, выдаваемой "наружу". Другими словами, в функциях Lua-интерфейса этот \0 появляться не должен.

Вопросы по использованию FFI

Posted: Sat 09 Sep, 2017 14:57
by DrKnS
Shmuel wrote: Sat 09 Sep, 2017 10:37 Другими словами, в функциях Lua-интерфейса этот \0 появляться не должен.
Да, действительно - по документации lua_pushlstring "makes (or reuses) an internal copy of the given string", теряя этот \0. Печально.

Вопросы по использованию FFI

Posted: Sat 09 Sep, 2017 15:19
by Shmuel
Печально, что главный автор Lua не согласился добавлять два "служебных" \0 вместо одного. (Причина, полагаю в том, что для него UTF-16 - экзотика, не стоящая внимания).

Добавить один (или сколько угодно) нулевых байтов к результату в LuaFAR совсем не проблема, но это будет некорректное преобразование.

Как вариант, можно было бы сделать дополнительные функции c Z ("zero") в конце имени, например Utf8ToUtf16Z.

Вопросы по использованию FFI

Posted: Sat 09 Sep, 2017 15:47
by John Doe
К сожалению это мало чем отличается от того, чтобы добавить ноль конкатенацией с аргументом функции..

Вопросы по использованию FFI

Posted: Tue 12 Sep, 2017 22:33
by John Doe
John Doe wrote: Tue 25 Feb, 2014 12:56 Ещё вопрос.
В буфере из примера выше находится C-строка неизвестной длины.
Как лучше всего перевести её в lua-строку?
Очевидно надо использовать ffi.string, но поскольку длина заранее неизвестна, то возможны варианты
- объявить и использовать C.lstrlenW
- перевести в строку весь буфер, и использовать match"(.-)%z"
Давно было, и тогда мне почему то не пришло в голову решение на Lua, типа такого:

Code: Select all

local wchar = ffi.cast("wchar_t*",ptr)
local len = 0
while wchar[len]~=0 do len = len+1 end
Пример полностью:

Вопросы по использованию FFI

Posted: Wed 13 Sep, 2017 17:28
by John Doe

Code: Select all

local ptr = ffi.new("char[?]",100,win.Utf8ToUtf16"test")
Похоже ffi.new тут работает не так, как я ожидал.
Из документации вроде бы следует, что ffi.new заполняет созданый массив нулями.
The elements of an array are initialized, starting at index zero. If a single initializer is given for an array, it's repeated for all remaining elements. This doesn't happen if two or more initializers are given: all remaining uninitialized elements are filled with zero bytes.
Однако в случае, когда в качестве инициализатора выступает строка, меньшая по длине, остаток видимо не обнуляется.
В документации этот частный случай не рассматривается, сказано только в общем:
Byte arrays may also be initialized with a Lua string. This copies the whole string plus a terminating zero-byte. The copy stops early only if the array has a known, fixed size.

Вопросы по использованию FFI

Posted: Wed 13 Sep, 2017 17:36
by Shmuel
John Doe wrote: Wed 13 Sep, 2017 17:28В документации этот частный случай не рассматривается, сказано только в общем
На мой взгляд, сказано исчерпывающе ("This copies the whole string plus a terminating zero-byte").

Вопросы по использованию FFI

Posted: Wed 13 Sep, 2017 17:43
by John Doe
Меня ввело в заблужнение то, что непосредственно перед этим сказано: all remaining uninitialized elements are filled with zero bytes.
А так да, не спорю.

Вопросы по использованию FFI

Posted: Wed 29 Nov, 2017 09:30
by John Doe
Заготовка для документации:

0. Использование ffi (помимо усвоения документации библиотеки) в целом требует определённой квалификации, понимания работы вызываемых функций, осторожного обращения с памятью, и т.п.
Ниже следуют только дополнения, описывающие особенности использования ffi в фаре

1. Любое определение, сделанное посредством ffi.cdef, действует до конца сеанса.
Переопределить его невозможно: будет действовать определение, сделанное первым.
1.1. Определение, сделанное в одном скрипте будет действовать для всех, поэтому важно чтобы определения были корректными.
1.2. В случае изменения определения на несовместимое потребуется перезагрузить плагин (или фар).

2. В отличии от переопределения функций, попытка переопределения констант и именованных(?) структур ещё и вызывает ошибку времени выполнения, поэтому следует оборачивать вызов в pcall:

2.1. Следует учитывать, что "переопределение" неизбежно, это вполне штатная ситуация: например при перезагрузке макросов, или при использовании определений в независимых скриптах.

3. Для удобства luamacro при старте загружает файл winapi.lua, содержащий определения типов, часто используемых в winapi.

4. ???

Работа со строками, с юникодом, передача параметра по ссылке или значению (_OUT_/_IN_ в msdn) это то, что абсолютно необходимо понимать при работе с ffi.

Прочитав тему с начала можно найти ответы на множество вопросов.
Если же вы не готовы тратить своё время на то чтобы во всём разобраться, то и начинать не стоит.

Todo: задокументировать нюансы, имеющие значение для распространённых задач:
- работа со строками, завершающий \0
- получение указателей/хэндлов, ...
- ограничение типов на 32бита: viewtopic.php?p=119951#p119951

Вопросы по использованию FFI

Posted: Wed 29 Nov, 2017 10:03
by Shmuel
John Doe wrote: Wed 29 Nov, 2017 09:301.2. В случае изменения определения на несовместимое потребуется перезагрузить плагин (или фар).
Или выполнить _G.RecreateLuaState = true.

Вопросы по использованию FFI

Posted: Thu 30 Nov, 2017 08:25
by Smitis
John Doe wrote: Wed 29 Nov, 2017 09:302. В отличии от переопределения функций, попытка переопределения констант и именованных(?) структур ещё и вызывает ошибку времени выполнения, поэтому следует оборачивать вызов в pcall:
В подавляющем большинстве случаев достаточно убрать имя структуры

Code: Select all

ffi.cdef [[
typedef struct { // _CONSOLE_SELECTION_INFO
  DWORD      dwFlags;
  COORD      dwSelectionAnchor;
  SMALL_RECT srSelection;
} CONSOLE_SELECTION_INFO, *PCONSOLE_SELECTION_INFO;
]]