FFI — одна из стандартных библиотек luajit.
Позволяет из Lua кода вызывать внешние C-функции и использовать структуры данных C.
В данной теме задаём вопросы по использованию FFI
int MessageBoxW(void *w, const char *txt, const char *cap, int type);
]]
C.MessageBoxW(nil, utf16('Тут у нас сообщение (message)'), utf16('Тут у нас титель (title)'),1)
Я конечно понимаю, что так делать не совсем чисто, но для машины-то без разницы, будь то char* byte* или short*.
Re: ffi.C и unicode/multibyte ... string
Posted: Mon 16 Dec, 2013 17:06
by HaRT
EGez wrote:Я конечно понимаю, что так делать не совсем чисто, но для машины-то без разницы, будь то char* byte* или short*.
Машина машине рознь. На многих RISC-архитектурах у char* и short* почти наверняка будет разное выравнивание, что чревато в лучшем случае просадкой производительности, а в типичном — “unaligned” exception. И да, некоторые из таких машин работают (работали?) под управлением тех или иных Windows.
Re: ffi.C и unicode/multibyte ... string
Posted: Tue 17 Dec, 2013 18:03
by EGez
HaRT wrote:Машина машине рознь. На многих RISC-архитектурах у char* и short* почти наверняка будет разное выравнивание, что чревато в лучшем случае просадкой производительности, а в типичном — “unaligned” exception. И да, некоторые из таких машин работают (работали?) под управлением тех или иных Windows.
Ну, совсем экзотикой можно принебречь...
Re: Вопросы по использованию FFI
Posted: Mon 24 Feb, 2014 15:41
by John Doe
Хотел составить функцию для превращения строки Lua в указатель, для передачи входных параметров в разные WinAPI-функции.
Сделал, работает:
local function str2p(str)
return ffi.cast("void*",win.Utf8ToUtf16(str .. '\0'))
end
Но перечитав тему засомневался, ведь результат win.Utf8ToUtf16 при выходе из функции становится мусором?
С другой стороны, полученный указатель и не требуется долго хранить...
В общем, на первой странице похожий пример рассматривался, решили что нельзя.
Если только не сохранить результат win.Utf8ToUtf16 в актуальную переменную. Или использовать ffi.new/ffi.copy
Хотя тогда я несовсем понимаю, почему в другом примере на первой странице делается так:
local PluginId = ffi.cast("const GUID*", win.Uuid("4EBBEFC8-2084-4B7F-94C0-692CE136894D"))
local MenuId = ffi.cast("const GUID*", win.Uuid("6C47D27E-3E7E-4C65-916D-E1B1A31527AD"))
--пропущено 10 строк кода
psi.Menu(PluginId,MenuId,-1,-1,0,0,nil,nil,nil,nil,nil,items,N)
Разве тут есть гарантия, что данные доживут до момента их использования?
И последний момент: всегда ли корректно кастить в "void*", не разделяя что там на самом деле требуется принимающим функциям
(const wchar_t* / wchar_t* / char* / и т.д.) ?
Re: Вопросы по использованию FFI
Posted: Mon 24 Feb, 2014 16:45
by Shmuel
ffi.cast от "незакреплённой" величины - это скорее всего баг. То, что он не всегда проявляется, тоже понятно. Чтобы стимулировать проявление бага, можно вызвать пару раз collectgarbage("collect") между получением и использованием.
Пример с гуидом - да, видимо баг.
ffi.cast("void*", value) для передачи функциям Windows API / Far API корректен, если value указывает на то, что функция ожидает получить.
local str = string.rep("\0",BufferLength)
local Buffer = ffi.cast("void*",str)
--
return win.Utf16ToUtf8(str)
?
Есть тут какие-то грабли?
И правильно ли я понимаю что ffi.new("wchar_t[?]",BufferLength/2) это то же самое что ffi.new("char[?]",BufferLength)
Т.е. по большому счёту тип не важен, главное выделять BufferLength/ffi.sizeof(type)?
Re: Вопросы по использованию FFI
Posted: Tue 25 Feb, 2014 11:55
by HaRT
John Doe wrote:правильно ли я понимаю что ffi.new("wchar_t[?]",BufferLength/2) это то же самое что ffi.new("char[?]",BufferLength)[...]?
Не знаю, как там в Lua/FFI, а в Си/Си++ это будет корректно только на конкретных платформах. На других же платформах значение Пи может достигать четырёх размер sizeof(wchar_t) может заметно отличаться.
Re: Вопросы по использованию FFI
Posted: Tue 25 Feb, 2014 12:08
by John Doe
HaRT
Потому я и указал что в общем случае зависит от sizeof.
Но на тех платформах, на которых вообще возможен запуск фара, этим можно пренебречь.
Во всяком случае такой вывод я делаю из примеров кода специалистов:
local str = string.rep("\0",BufferLength)
local Buffer = ffi.cast("void*",str)
--
return win.Utf16ToUtf8(str)
?
Есть тут какие-то грабли?
И правильно ли я понимаю что ffi.new("wchar_t[?]",BufferLength/2) это то же самое что ffi.new("char[?]",BufferLength)
Т.е. по большому счёту тип не важен, главное выделять BufferLength/ffi.sizeof(type)?
1. Граблей не вижу, но не забывать о дополнительном нулевом символе в конце.
2. Правильно, при условии, что ф-ция API получит на вход тот тип указателя, с которым она объявлена (или void*).
Re: Вопросы по использованию FFI
Posted: Tue 25 Feb, 2014 12:43
by zg
John Doe wrote:Есть тут какие-то грабли?
потенциально есть грабли с выравниванием.
Re: Вопросы по использованию FFI
Posted: Tue 25 Feb, 2014 12:56
by John Doe
zg
Т.е. адрес, по которому находится lua-строка, может прийтись не на начало машинного слова?
Выходит не стоит так делать.
Ещё вопрос.
В буфере из примера выше находится C-строка неизвестной длины.
Как лучше всего перевести её в lua-строку?
Очевидно надо использовать ffi.string, но поскольку длина заранее неизвестна, то возможны варианты
- объявить и использовать C.lstrlenW
- перевести в строку весь буфер, и использовать match"(.-)%z"
Почему-то кажется что должен быть более изящный способ.
И вроде бы сказано:
str = ffi.string(ptr [,len]) wrote:If the optional argument len is missing, ptr is converted to a "char *" and the data is assumed to be zero-terminated. The length of the string is computed with strlen().
Однако о типах C у меня весьма отдалённое представление, поэтому не уверен подходит ли это к моему случаю или нет (пробовал в лоб - не работает).
Хотя вряд ли, wchar ведь может содержать нуль
Re: Вопросы по использованию FFI
Posted: Tue 25 Feb, 2014 18:17
by zg
John Doe wrote:Т.е. адрес, по которому находится lua-строка, может прийтись не на начало машинного слова?
в теории, да. на практике, наверное, нет. но кто ж его знает, что случится завтра.
John Doe wrote:- объявить и использовать C.lstrlenW
Почему-то кажется что должен быть более изящный способ.
я так делал в спелере, вроде даже что-то обсуждали тогда, и типа нет вариантов проще.
Re: Вопросы по использованию FFI
Posted: Tue 25 Feb, 2014 23:50
by John Doe
zg wrote:
John Doe wrote:Т.е. адрес, по которому находится lua-строка, может прийтись не на начало машинного слова?
в теории, да. на практике, наверное, нет. но кто ж его знает, что случится завтра.
Значит и пример с первой страницы не полностью надёжен?
спасибо, поправил. update: тут пишут, что «8 byte alignment is guaranteed» для ffi.new.
Re: Вопросы по использованию FFI
Posted: Sun 02 Mar, 2014 07:46
by John Doe
Возможно ли через ffi как-то воспользоваться C-функциями far api?
Re: Вопросы по использованию FFI
Posted: Sun 02 Mar, 2014 08:09
by Shmuel
John Doe wrote:Возможно ли через ffi как-то воспользоваться C-функциями far api?
Да, для этого даже добавлен файл LuaMacro\farapi.lua.
Для получения указателя на PluginStartupInfo есть ф-ция far.CPluginStartupInfo, нужно cделать ffi.cast() от результата её вызова.