Page 2 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

Re: Ликбез по Lua

Posted: Tue 16 Jul, 2013 21:20
by Shmuel

Latest post of the previous page:

Я в своё время просил Роберто (в форуме Lua, можно найти) добавлять два нуля к стрингам вместо одного, но он не пошёл на это.

Re: luajit в фаре

Posted: Thu 08 Aug, 2013 02:56
by zg
Shmuel wrote:В своих библиотеках можно перед вызовом ffi.cdef проверить, определена ли та или иная константа.
а как?

luajit в фаре

Posted: Thu 08 Aug, 2013 07:14
by Shmuel
zg wrote:а как?
Например, так:
  1. local ffi = require "ffi"
  2.  
  3. if pcall(ffi.sizeof, "FFI_FARAPI_DEFINED") then return end
  4.  
  5. ffi.cdef "typedef int FFI_FARAPI_DEFINED"

Re: luajit в фаре

Posted: Sun 11 Aug, 2013 17:00
by Acerbic
Shmuel wrote: LuaJIT FFI рассматривает переопределение статических констант и enum'ов как ошибку. На повторное определение функций почему-то не жалуется.
Это в С такая фишка, что объявление и определение - разные вещи, ЕМНИП. Определения повторять нельзя, объявления можно. Для функций заголовок без тела считается только объявлением, а константы и переменные (даже не инициализированные) считаются определениями, если нет специального ключевого слова extern

Re: luajit в фаре

Posted: Sun 11 Aug, 2013 17:23
by Shmuel
Acerbic wrote:Это в С такая фишка, что объявление и определение - разные вещи, ЕМНИП. Определения повторять нельзя, объявления можно. Для функций заголовок без тела считается только объявлением, а константы и переменные (даже не инициализированные) считаются определениями, если нет специального ключевого слова extern
Да, это так, но при использовании в Lua доставляет неудобства: нельзя (без спец-приседаний) запустить скрипт более 1 раза, нельзя перезагрузить модуль.

ffi.C и unicode/multibyte ... string

Posted: Sat 14 Dec, 2013 17:39
by EGez
Что-то никак не получается через FFI протолкнуть кириллический текст без потерь, уже даже к методам научного тыка прибегнуть пришлось ... :(

На примере: MessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT);

Пока пришел к следующему, но в конце текста сообщения мусор, как правильно с ним работать?
  1. -- file encoding UTF-8/65001
  2.  
  3.  
  4.  
  5. local ffi = require("ffi")
  6.  
  7. local C = ffi.C
  8.  
  9.  
  10.  
  11. ffi.cdef[[
  12.  
  13. int MessageBoxA(void *w, const char *txt, const char *cap, int type);
  14.  
  15. int MessageBoxW(void *w, const wchar_t *txt, const wchar_t *cap, int type);
  16.  
  17. ]]
  18.  
  19.  
  20.  
  21. -- MessageBoxW:
  22.  
  23. -- 1. Китайский
  24.  
  25. C.MessageBoxW(nil, 'Кириллический текст', 'AS IS', 1)
  26.  
  27.  
  28.  
  29. -- 2. Вроде работает, но в конце строк мусор
  30.  
  31. C.MessageBoxW(nil, win.Utf8ToUtf16('Кириллический текст'), win.Utf8ToUtf16('Utf8ToUtf16'), 1)
  32.  
  33.  
  34.  
  35. -- 3. Снова китай
  36.  
  37. C.MessageBoxW(nil, win.OemToUtf8('Кириллический текст'), win.OemToUtf8('OemToUtf8'), 1)
  38.  
  39.  
  40.  
  41.  
  42.  
  43. -- MessageBoxA:
  44.  
  45. -- 4. UTF-8 в ANSI кодировке
  46.  
  47. C.MessageBoxA(nil, 'Кириллический текст', 'AS IS', 1)
Последний случай (MessageBoxA) у большинства из вас скорее всего будет работать, т.к. locale у вас "случайно" подходящая...

Благодарю

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

Posted: Sat 14 Dec, 2013 17:55
by Abs
EGez wrote:-- 2. Вроде работает, но в конце строк мусор
C.MessageBoxW(nil, win.Utf8ToUtf16('Кириллический текст'), win.Utf8ToUtf16('Utf8ToUtf16'), 1)

Code: Select all

C.MessageBoxW(nil, win.Utf8ToUtf16('Кириллический текст\0'), win.Utf8ToUtf16('Utf8ToUtf16\0'), 1)
?

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

Posted: Sat 14 Dec, 2013 19:09
by EGez
Abs wrote:

Code: Select all

C.MessageBoxW(nil, win.Utf8ToUtf16('Кириллический текст\0'), win.Utf8ToUtf16('Utf8ToUtf16\0'), 1)
?
Спасибо, сработало.

Но меня терзают смутные сомнения: Почему jit их автоматически не завершает нулями при передаче в контекст c runtime? Это же стандарт в C/C++, именно поэтому мне и не пришла такая идея в голову.
Это workaround или стандартное поведение? Где-нибудь описано?

Спасибо

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

Posted: Sat 14 Dec, 2013 19:37
by zg
EGez wrote:Но меня терзают смутные сомнения: Почему jit их автоматически не завершает нулями при передаче в контекст c runtime?
а как он должен догадаться?

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

Posted: Sat 14 Dec, 2013 19:47
by Shmuel
EGez wrote:Но меня терзают смутные сомнения: Почему jit их автоматически не завершает нулями при передаче в контекст c runtime? Это же стандарт в C/C++, именно поэтому мне и не пришла такая идея в голову.
Это workaround или стандартное поведение? Где-нибудь описано?
LuaJIT обрабатывает то, что ему передано, и это правильно.

Данное поведение проистекает от того, что, в отличие от Си-строк, строки в Lua не содержат завершающего нуля (хотя во внутренностях Lua он имеется, но только один нулевой байт, а не два, как нужно для UTF-16).

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

Posted: Sat 14 Dec, 2013 20:16
by EGez
Shmuel wrote: LuaJIT обрабатывает то, что ему передано, и это правильно.

Данное поведение проистекает от того, что, в отличие от Си-строк, строки в Lua не содержат завершающего нуля (хотя во внутренностях Lua он имеется, но только один нулевой байт, а не два, как нужно для UTF-16).
Хм.. Я кажется понял в чем дело. Скорее дело не в том, что Lua не использует zero termination.
Поскольку Lua стринги свои не различает на широкие и узкие, ;-) в C runtime оно, похоже, передается как char[]/byte[] совершенно не задумываясь о семантике содержимого и все-таки завершенным одним \0 (т.к. char). Поэтому ANSI функции работают нормально... А функция же, которая получает от сего пойнтер параметром, ожидает, что это utf16 и интерпретирует его соответственно, ну и обламывается на одном \0.

Спасибо!

ffi.C и unicode/multibyte ... string

Posted: Mon 16 Dec, 2013 09:57
by John Doe
EGez wrote:Что-то никак не получается через FFI протолкнуть кириллический текст без потерь, уже даже к методам научного тыка прибегнуть пришлось ... :(

На примере: MessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT);

Последний случай (MessageBoxA) у большинства из вас скорее всего будет работать, т.к. locale у вас "случайно" подходящая...

Благодарю
Что тут может быть не так?

Code: Select all

C:\APPs\Far3\lua51x.exe: C:\APPs\Far3\Profile\Macros\2check\ffi\MessageBoxW.lua:13: bad argument #2
to 'MessageBoxW' (cannot convert 'string' to 'const unsigned short *')
stack traceback:
        [C]: in function 'MessageBoxW'
        C:\APPs\Far3\Profile\Macros\2check\ffi\MessageBoxW.lua:13: in main chunk
        [C]: at 0x00407bbc
Проблема только с MessageBoxW, а MessageBoxA отрабатывает нормально

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

Posted: Mon 16 Dec, 2013 10:14
by Shmuel
John Doe wrote:Что тут может быть не так?

Code: Select all

C:\APPs\Far3\lua51x.exe: C:\APPs\Far3\Profile\Macros\2check\ffi\MessageBoxW.lua:13: bad argument #2
to 'MessageBoxW' (cannot convert 'string' to 'const unsigned short *')
stack traceback:
        [C]: in function 'MessageBoxW'
        C:\APPs\Far3\Profile\Macros\2check\ffi\MessageBoxW.lua:13: in main chunk
        [C]: at 0x00407bbc
В тексте ошибки ясно сказано, что не так. wchar_t - это unsigned short.
Почитайте несколько сообщений, начиная от http://forum.farmanager.com/viewtopic.p ... 45#p111545, там есть примеры.

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

Posted: Mon 16 Dec, 2013 10:25
by John Doe
В данном случае я не пытался освоить FFI, а всего лишь хотел запустить готовый пример из первого сообщения.
Автор представляет его как рабочий, поэтому мне показалось что у меня какая-то проблема с инсталляцией.

ffi.C и unicode/multibyte ... string

Posted: Mon 16 Dec, 2013 10:34
by Shmuel
Можно так:
  1. C.MessageBoxW(nil, ffi.cast("void*", win.Utf8ToUtf16('Кириллический текст\0')),
  2.  
  3.    ffi.cast("void*", win.Utf8ToUtf16('Utf8ToUtf16\0')), 1)

ffi.C и unicode/multibyte ... string

Posted: Mon 16 Dec, 2013 17:03
by EGez
Пардон, я наверху что-то напутал походу с 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*.