Получение «указателя» на функцию Lua, хранящуюся в C

В Lua C API я могу сохранить число или строку из стека с помощью lua_tostring().

Как «ссылка» (если это корректный термин) на функцию Lua может быть передана в C через Lua API? Таким образом, его можно вызвать позже из C с помощью lua_call(), без необходимости ссылаться на него по имени.

(Это действительно должно быть так, программа C вызовет функцию где-то в будущем, и программа ничего не знает о функции, потому что передаваемые функции определены в программе Lua)


person Community    schedule 22.05.2014    source источник
comment
Что-то вроде этого? colberg.org/gcc-lua-cdecl/ffi-cdecl.html   -  person Robert Harvey    schedule 22.05.2014
comment
@RobertHarvey не похоже на то, что я хочу, если вы знаете соответствующие части о том, как это сделать, опубликуйте их как ответ, пожалуйста.   -  person    schedule 22.05.2014


Ответы (1)


В C вы не можете обращаться к функциям Lua напрямую, но можете представлять числа и строки. Итак, чтобы функция "вызывалась позже", можно хранить эту функцию в какой-нибудь таблице и обращаться к ней по числовому или строковому ключу таблицы.

Вот простой механизм для начала:

На стороне Луа:

funcs = {}

local function register_hanlder(key, fn)
  funcs[key] = fn
end

register_handler("on_mouse_click", function()
  print "You clicked me!"
end)

На стороне С:

/* code not tested */
lua_getglobal(L, "funcs");
lua_getfield(L, -1, "on_mouse_click");
if (!lua_isnil(L, -1)) {
  lua_call(L, 0, 0);
else {
  // nothing registered
}

Вместо регистрации функций в глобальной таблице вы можете зарегистрировать их в таблице реестра (см. luaL_ref). Вы получите некоторое целое число (это ключ в таблице реестра, где находится значение функции), которое вы можете передать в своем коде C.

Обратите внимание, что если вам не нужно хранить функцию Lua «для использования позже», вам не нужно ничего из этого: если ваша функция C имеет какую-то функцию Lua, переданную ей через аргумент, вы можете вызвать ее напрямую.

== Изменить:

Как я уже упоминал, вместо использования глобальной переменной (funcs выше) вы можете хранить ссылку на функцию в «реестре». Концептуально нет никакой разницы между этим методом и предыдущим.

Давайте воспользуемся предыдущим примером: вы хотите, чтобы программист Lua мог зарегистрировать функцию, которая будет запускаться при каждом щелчке мыши в вашем приложении.

Сторона Lua будет выглядеть так:

register_mouse_click_handler(function()
  print "the mouse was clicked!"
end)

На стороне C вы определяете register_mouse_click_handler:

static int the_mouse_click_handler = 0;

static int register_mouse_click_handler(lua_State* L) {
  the_mouse_click_handler = luaL_ref(L, LUA_REGISTRYINDEX);
  return 0;
}

(... и открыть его для Lua.)

Затем в вашем приложении, когда вы щелкаете мышью и хотите вызвать функцию Lua, вы делаете:

...
if (the_mouse_click_handler != 0) {
  lua_rawgeti(L, LUA_REGISTRYINDEX, the_mouse_click_handler);
  lua_call(L, 0, 0);
} else {
  // No mouse handler was registered.
}
...

(Могут быть опечатки в коде.)

person Niccolo M.    schedule 22.05.2014
comment
Спасибо за ваш ответ, но извините, если мой реальный вопрос немного отличается. На самом деле я получаю указатель на функцию lua, хранящуюся в списке, который читается из C. Дело в том, что из CI на самом деле не знаю, какая функция должна быть передана, я просто хочу сохранить ссылку на нее, поэтому я можно вызвать из C. Как бы вы это сделали? (Извините за изменение) - person ; 23.05.2014
comment
@Alec: Я снова и снова читал ваши вопросы и комментарии, и кажется, что это именно тот вопрос, на который я ответил. Я отредактировал свой ответ, включив в него решение с использованием реестра, но концепция та же. Я не добавил ничего действительно нового. Позвольте мне повторить: вы не можете напрямую хранить в C ссылку на функцию Lua. Ссылка хранится на стороне Lua. То, что у вас есть на вашей стороне C, — это некий ключ, который позволяет вам извлекать (вталкивать) функцию Lua в стек. - person Niccolo M.; 23.05.2014
comment
Это действительно имеет значение, в моем конкретном сценарии я понятия не имею, что находится на стороне Lua, код полностью не контролируется мной (это моды для игры), где я позволяю разработчику мода указывать вызываемые функции. Вот почему ваш второй метод работает для меня. - person ; 23.05.2014