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

Столкнулись с проблемой в использовании макросов? Здесь можно получить помощь.
User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post by John Doe »

FFI — одна из стандартных библиотек luajit.
Позволяет из Lua кода вызывать внешние C-функции и использовать структуры данных C.
В данной теме задаём вопросы по использованию FFI

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

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

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post 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

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post by John Doe »

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

Code: Select all

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

Shmuel
Posts: 6024
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 26 times
Been thanked: 284 times

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

Post by Shmuel »

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

Smitis
Posts: 1611
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 18 times
Been thanked: 39 times

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

Post by Smitis »

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

User avatar
DrKnS
Posts: 5513
Joined: Thu 04 Aug, 2005 06:44
Location: Kyiv
Has thanked: 6 times
Been thanked: 279 times

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

Post by DrKnS »

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

Shmuel
Posts: 6024
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 26 times
Been thanked: 284 times

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

Post by Shmuel »

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

User avatar
DrKnS
Posts: 5513
Joined: Thu 04 Aug, 2005 06:44
Location: Kyiv
Has thanked: 6 times
Been thanked: 279 times

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

Post 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. Печально.

Shmuel
Posts: 6024
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 26 times
Been thanked: 284 times

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

Post by Shmuel »

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

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

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

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post by John Doe »

К сожалению это мало чем отличается от того, чтобы добавить ноль конкатенацией с аргументом функции..

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post 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
Пример полностью:
Last edited by John Doe on Wed 13 Sep, 2017 18:00, edited 1 time in total.

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post 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.

Shmuel
Posts: 6024
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 26 times
Been thanked: 284 times

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

Post by Shmuel »

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

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post by John Doe »

Меня ввело в заблужнение то, что непосредственно перед этим сказано: all remaining uninitialized elements are filled with zero bytes.
А так да, не спорю.

User avatar
John Doe
Бюрократ
Posts: 12648
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 268 times

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

Post 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.

Прочитав тему с начала можно найти ответы на множество вопросов.
Если же вы не готовы тратить своё время на то чтобы во всём разобраться, то и начинать не стоит.
Last edited by John Doe on Sat 12 May, 2018 16:50, edited 1 time in total.

Shmuel
Posts: 6024
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 26 times
Been thanked: 284 times

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

Post by Shmuel »

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

Smitis
Posts: 1611
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 18 times
Been thanked: 39 times

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

Post by Smitis »

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

Code: Select all

ffi.cdef [[
typedef struct { // _CONSOLE_SELECTION_INFO
  DWORD      dwFlags;
  COORD      dwSelectionAnchor;
  SMALL_RECT srSelection;
} CONSOLE_SELECTION_INFO, *PCONSOLE_SELECTION_INFO;
]]
Разум когда-нибудь победит.

Post Reply

Return to “Нужна помощь”