Я люблю Lua. I love Lua.

Вызов анонимной Lua функции из С++

Мне понадобилось решить следующую задачу:
для окна в моем GUI нужно задать функцию, которая будет вызвана в результате нажетия некоей комбинации кнопок(«горячие кнопки», hot keys). Окна GUI создаются в Lua, соответственно и функция, которая должна вызываться — это Lua функция.
Проблема в том, что функция может быть локальной.
Установка калбека у окна может выглядеть так:

window:setCallback(function () print('Hi from callback!') end, 'left alt', 'a')

Соответственно, функция регистрации калбека должна выглядеть как-то так:

int windowSetCallback(lua_State* L)
{
  // stack: window callback key1 key2 key3 key4
  ...
  return 0;
}

Первый параметр на вершине стека — это переданный из Lua указатель на окно.
Второй параметр — некая, возможно безымянная, Lua функция. Ее нужно как-то сохранить, для дальнейшего использования.
Далее идут строковые параметры — список кнопок для комбинации.
Для хранения в С временных Lua объектов используется реестр.
Для того, чтобы сохранить ссылку на Lua объект, находящийся на вершине стека,
нужно вызвать функцию

int luaL_ref (lua_State *L, int t);

с параметром

t = LUA_REGISTRYINDEX

которая сохранит ссылку на объект в специальной внутренней таблице Lua(реестре) и вернет индекс,
по которому в дальнейшем можно будет обратиться, чтобы получить доступ к объекту.
Функция:

luaL_ref()

выталкивает объект из стека и возвращает индекс ссылки на него.
Функция:

void lua_rawgeti (lua_State *L, int index, int n);

вызванная с параметром

index = LUA_REGISTRYINDEX

поместит на стек объект, находящийся в этой таблице по индексу n.
Если временный Lua объект больше не нужен, то ссылку на него можно удалить при помощи функции

void luaL_unref (lua_State *L, int t, int ref);

Итак, в моем случае функция регистрации Lua калбека выглядит так:

int windowSetCallback(lua_State* L)
{
  // stack: window callback key1 key2 key3 key4
  WIDGET_CAST(WindowBase, window, L, 1);

  // помещаем калбек на вершину стека
  lua_pushvalue(L, 2); // stack: window callback key1 key2 key3 key4 callback

  int callbackRef = luaL_ref(L, LUA_REGISTRYINDEX); // stack: window callback key1 key2 key3 key4
  // вызов luaL_ref() вытолкнет калбек с вершины стека

  // список кнопок
  WindowBase::HotKeys hotKeys;
  std::string key;

  for(int i = 3; 7 > i; ++i)
  {
    if(getValue(L, i, key))
    {
      hotKeys.insert(getKeyboardButtonByName(key.c_str()));
    }
  }
  // установка калбека для окна
  // первый параметр - указатель на функцию - нам он не нужен
  // второй параметр - список кнопок
  // третий параметр - указатель void*, который будет передан в функцию,
  // указатель на которую задан первым параметром.
  // мы в этом параметре сохраним ссылку передадим ссылку на безымянную Lua функцию.
  window->setHotKey(0, hotKeys, (void*)callbackRef);

  return 0;
}

Далее в обработчике горячих кнопок окна:

bool LuaWindow::onHotKey_(const HotKeyCombo& combo) // virtual
{
  // приводим void* к int
  int callbackRef = (int)combo.param_;

  lua_rawgeti(L_, LUA_REGISTRYINDEX, callbackRef); // stack: callback

  // убедимся, что это функция
  if(lua_isfunction(L_, -1))
  {
    // вызов lua_pcall с обработкой ошибок
    callLuaFunction(L_, 0, 0);
  }

  return true;
}
Реклама

комментария 2

Subscribe to comments with RSS.

  1. Виталий said, on Январь 11, 2014 at 8:23 дп

    Хорошая и простая статья. У меня была проблема,мой биндер экспортировал классы, в нем были стандартные геттеры и сетеры для __index и __newindex соответственно, но если я добавлял в луа скрипте какую-то функцию к моему экспортируемому классу, то происходили вылеты с ошибками … кто делал тот знает какие ошибки, но прочитав статью, слава Богу понял и сделал хранение луа функции, теперь все работает так как и хотел!


Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: