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

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

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

Post by John Doe »

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

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

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

Предупреждение: viewtopic.php?p=147610#p147610
EGez
Posts: 145
Joined: Thu 04 Oct, 2012 13:53
Location: Hamburg, DE
Has thanked: 2 times
Been thanked: 2 times

ffi.C и unicode/multibyte ... string

Post by EGez »

Latest post of the previous page:

Пардон, я наверху что-то напутал походу с ffi.cdef, оно же второй раз не переопределяется и об ошибке не сообщает, нужно было Фар перегружать.

На самом деле у меня работающий пример выглядит так:
  1. -- file encoding UTF-8/65001
  2.  
  3.  
  4.  
  5. local ffi = require 'ffi'
  6.  
  7. local C = ffi.C
  8.  
  9.  
  10.  
  11. local function utf16(str)
  12.  
  13.     return win.Utf8ToUtf16(str .. '\0')
  14.  
  15. end
  16.  
  17.  
  18.  
  19. ffi.cdef[[
  20.  
  21. int MessageBoxW(void *w, const char *txt, const char *cap, int type);
  22.  
  23. ]]
  24.  
  25.  
  26.  
  27. C.MessageBoxW(nil, utf16('Тут у нас сообщение (message)'), utf16('Тут у нас титель (title)'), 1)
Я конечно понимаю, что так делать не совсем чисто, но для машины-то без разницы, будь то char* byte* или short*.
User avatar
HaRT
Moderator
Posts: 10806
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 220 times
Been thanked: 357 times

Re: ffi.C и unicode/multibyte ... string

Post by HaRT »

EGez wrote:Я конечно понимаю, что так делать не совсем чисто, но для машины-то без разницы, будь то char* byte* или short*.
Машина машине рознь. На многих RISC-архитектурах у char* и short* почти наверняка будет разное выравнивание, что чревато в лучшем случае просадкой производительности, а в типичном — “unaligned” exception. И да, некоторые из таких машин работают (работали?) под управлением тех или иных Windows.
Фар есть инструмент, а не нянька. © 2009 DrKnS
EGez
Posts: 145
Joined: Thu 04 Oct, 2012 13:53
Location: Hamburg, DE
Has thanked: 2 times
Been thanked: 2 times

Re: ffi.C и unicode/multibyte ... string

Post by EGez »

HaRT wrote:Машина машине рознь. На многих RISC-архитектурах у char* и short* почти наверняка будет разное выравнивание, что чревато в лучшем случае просадкой производительности, а в типичном — “unaligned” exception. И да, некоторые из таких машин работают (работали?) под управлением тех или иных Windows.
Ну, совсем экзотикой можно принебречь...
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Хотел составить функцию для превращения строки Lua в указатель, для передачи входных параметров в разные WinAPI-функции.
Сделал, работает:

Code: Select all

local function str2p(str)
  return ffi.cast("void*",win.Utf8ToUtf16(str .. '\0'))
end
Но перечитав тему засомневался, ведь результат win.Utf8ToUtf16 при выходе из функции становится мусором?
С другой стороны, полученный указатель и не требуется долго хранить...

В общем, на первой странице похожий пример рассматривался, решили что нельзя.
Если только не сохранить результат win.Utf8ToUtf16 в актуальную переменную. Или использовать ffi.new/ffi.copy

Хотя тогда я несовсем понимаю, почему в другом примере на первой странице делается так:

Code: Select all

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* / и т.д.) ?
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

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

Post by Shmuel »

ffi.cast от "незакреплённой" величины - это скорее всего баг. То, что он не всегда проявляется, тоже понятно. Чтобы стимулировать проявление бага, можно вызвать пару раз collectgarbage("collect") между получением и использованием.

Пример с гуидом - да, видимо баг.

ffi.cast("void*", value) для передачи функциям Windows API / Far API корректен, если value указывает на то, что функция ожидает получить.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Спасибо.

А этот код можно ли заменить на ?
Есть тут какие-то грабли?

И правильно ли я понимаю что ffi.new("wchar_t[?]",BufferLength/2) это то же самое что ffi.new("char[?]",BufferLength)
Т.е. по большому счёту тип не важен, главное выделять BufferLength/ffi.sizeof(type)?
User avatar
HaRT
Moderator
Posts: 10806
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 220 times
Been thanked: 357 times

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

Post by HaRT »

John Doe wrote:правильно ли я понимаю что ffi.new("wchar_t[?]",BufferLength/2) это то же самое что ffi.new("char[?]",BufferLength)[...]?
Не знаю, как там в Lua/FFI, а в Си/Си++ это будет корректно только на конкретных платформах. На других же платформах значение Пи может достигать четырёх размер sizeof(wchar_t) может заметно отличаться.
Фар есть инструмент, а не нянька. © 2009 DrKnS
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

HaRT
Потому я и указал что в общем случае зависит от sizeof.
Но на тех платформах, на которых вообще возможен запуск фара, этим можно пренебречь.
Во всяком случае такой вывод я делаю из примеров кода специалистов:
spell.lua wrote:

Code: Select all

local function ToWChar(str)
  str=win.Utf8ToUtf16(str)
  local result=ffi.new("wchar_t[?]",#str/2+1)
  ffi.copy(result,str)
  return result
end
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

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

Post by Shmuel »

John Doe wrote:А этот код можно ли заменить на ?
Есть тут какие-то грабли?

И правильно ли я понимаю что ffi.new("wchar_t[?]",BufferLength/2) это то же самое что ffi.new("char[?]",BufferLength)
Т.е. по большому счёту тип не важен, главное выделять BufferLength/ffi.sizeof(type)?
1. Граблей не вижу, но не забывать о дополнительном нулевом символе в конце.
2. Правильно, при условии, что ф-ция API получит на вход тот тип указателя, с которым она объявлена (или void*).
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

John Doe wrote:Есть тут какие-то грабли?
потенциально есть грабли с выравниванием.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

zg
Т.е. адрес, по которому находится lua-строка, может прийтись не на начало машинного слова?
Выходит не стоит так делать.

Ещё вопрос.
В буфере из примера выше находится C-строка неизвестной длины.
Как лучше всего перевести её в lua-строку?
Очевидно надо использовать ffi.string, но поскольку длина заранее неизвестна, то возможны варианты
- объявить и использовать C.lstrlenW
- перевести в строку весь буфер, и использовать match"(.-)%z"

Почему-то кажется что должен быть более изящный способ.
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

John Doe wrote:Т.е. адрес, по которому находится lua-строка, может прийтись не на начало машинного слова?
в теории, да. на практике, наверное, нет. но кто ж его знает, что случится завтра.
John Doe wrote:- объявить и использовать C.lstrlenW

Почему-то кажется что должен быть более изящный способ.
я так делал в спелере, вроде даже что-то обсуждали тогда, и типа нет вариантов проще.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

zg wrote:
John Doe wrote:Т.е. адрес, по которому находится lua-строка, может прийтись не на начало машинного слова?
в теории, да. на практике, наверное, нет. но кто ж его знает, что случится завтра.
Значит и пример с первой страницы не полностью надёжен?
  1. local text = win.Utf8ToUtf16(str)
  2.  
  3. item.Text = ffi.cast("const wchar_t*", text)
Shmuel wrote:ffi.cast от "незакреплённой" величины - это скорее всего баг.
Нашёл в picture.lua:204 такой код:

Code: Select all

      local delayraw=ffi.cast("PropertyItem*",ffi.new("char[?]",delaysize[0]))
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

John Doe wrote:Значит и пример с первой страницы не полностью надёжен?
вопрос в том, как работает аллокатор строк и ffi.new. возможно там такое выравнивание, что хватит всем.
John Doe wrote:Нашёл в picture.lua:204 такой код:
спасибо, поправил.
update:
тут пишут, что «8 byte alignment is guaranteed» для ffi.new.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Возможно ли через ffi как-то воспользоваться C-функциями far api?
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

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

Post by Shmuel »

John Doe wrote:Возможно ли через ffi как-то воспользоваться C-функциями far api?
Да, для этого даже добавлен файл LuaMacro\farapi.lua.
Для получения указателя на PluginStartupInfo есть ф-ция far.CPluginStartupInfo, нужно cделать ffi.cast() от результата её вызова.
Post Reply

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