Вопросы по использованию 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
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

Re: Ликбез по Lua

Post by Shmuel »

Latest post of the previous page:

Я в своё время просил Роберто (в форуме Lua, можно найти) добавлять два нуля к стрингам вместо одного, но он не пошёл на это.
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

Re: luajit в фаре

Post by zg »

Shmuel wrote:В своих библиотеках можно перед вызовом ffi.cdef проверить, определена ли та или иная константа.
а как?
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

luajit в фаре

Post 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"
Acerbic
Posts: 80
Joined: Wed 07 Apr, 2010 12:30
Been thanked: 2 times

Re: luajit в фаре

Post by Acerbic »

Shmuel wrote: LuaJIT FFI рассматривает переопределение статических констант и enum'ов как ошибку. На повторное определение функций почему-то не жалуется.
Это в С такая фишка, что объявление и определение - разные вещи, ЕМНИП. Определения повторять нельзя, объявления можно. Для функций заголовок без тела считается только объявлением, а константы и переменные (даже не инициализированные) считаются определениями, если нет специального ключевого слова extern
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

Re: luajit в фаре

Post by Shmuel »

Acerbic wrote:Это в С такая фишка, что объявление и определение - разные вещи, ЕМНИП. Определения повторять нельзя, объявления можно. Для функций заголовок без тела считается только объявлением, а константы и переменные (даже не инициализированные) считаются определениями, если нет специального ключевого слова extern
Да, это так, но при использовании в Lua доставляет неудобства: нельзя (без спец-приседаний) запустить скрипт более 1 раза, нельзя перезагрузить модуль.
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 »

Что-то никак не получается через 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 у вас "случайно" подходящая...

Благодарю
Abs
Posts: 72
Joined: Tue 27 Jul, 2010 12:14
Been thanked: 7 times

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

Post 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)
?
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 »

Abs wrote:

Code: Select all

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

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

Спасибо
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

EGez wrote:Но меня терзают смутные сомнения: Почему jit их автоматически не завершает нулями при передаче в контекст c runtime?
а как он должен догадаться?
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

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

Post by Shmuel »

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

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

Shmuel wrote: LuaJIT обрабатывает то, что ему передано, и это правильно.

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

Спасибо!
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

ffi.C и unicode/multibyte ... string

Post 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 отрабатывает нормально
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

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

Post 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, там есть примеры.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

В данном случае я не пытался освоить FFI, а всего лишь хотел запустить готовый пример из первого сообщения.
Автор представляет его как рабочий, поэтому мне показалось что у меня какая-то проблема с инсталляцией.
Shmuel
Posts: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

ffi.C и unicode/multibyte ... string

Post by Shmuel »

Можно так:
  1. C.MessageBoxW(nil, ffi.cast("void*", win.Utf8ToUtf16('Кириллический текст\0')),
  2.  
  3.    ffi.cast("void*", win.Utf8ToUtf16('Utf8ToUtf16\0')), 1)
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 »

Пардон, я наверху что-то напутал походу с 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*.
Post Reply

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