Вызов функции Lua C возвращает ноль

Я написал простой плагин C для Lua:

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

static int bar (lua_State *L) {
  double arg1 = luaL_checknumber(L, 1);
  double arg2 = luaL_checknumber(L, 2);
  lua_Number res = arg1 + arg2;
  lua_pushnumber(L, res);
  return 1;
}

int luaopen_foo (lua_State *L) {
  static const struct luaL_Reg foo [] = {
    {"bar", bar},
    {NULL, NULL}
  };
  lua_newtable(L);
  luaL_setfuncs(L, foo, 0);
  lua_setglobal(L, "foo");
  return 1;
}

Код успешно скомпилирован с помощью этой команды GCC:

gcc -W -Wall -g -fPIC -shared -I/usr/include/lua5.3 -o foobar.so foobar.c

В REPL Lua 5.3 я также могу успешно найти и импортировать модуль, но возвращаемое значение вызова функции всегда равно nil:

root@b1898c1cc270:/# lua5.3
Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio
> local foo = require "foo"
> local res = foo.bar(3.0, 6.0)
> res
nil

Ошибки не выдаются, и, поскольку я могу printf получить результат в коде C перед возвратом значения, я знаю, что код вызывается и результат вычисляется успешно.

Любые идеи?

Edit1: не используя локальные переменные, я получаю эту трассировку стека вместо нулевого значения:

root@d7340c919be4:/# lua5.3
Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio
> foo = require "foo"
> res = foo.bar(3.0, 6.0)
stdin:1: attempt to call a nil value (field 'bar')
stack traceback:
    stdin:1: in main chunk
    [C]: in ?

person m_vdbeek    schedule 06.04.2018    source источник
comment
Не используйте local в сеансе интерактивного интерпретатора.   -  person siffiejoe    schedule 06.04.2018
comment
Кроме того, lua_setglobal извлекает вашу таблицу модулей из стека Lua, поэтому вы не можете return 1 из вашей luaopen_foo функции, если вы сначала не продублируете слот стека с помощью lua_pushvalue.   -  person siffiejoe    schedule 06.04.2018
comment
@siffiejoe Спасибо за быстрый ответ. Я отредактировал свой вопрос с помощью трассировки стека, которую я получаю, когда не использую локальные переменные. Есть ли причины не использовать их в интерпретаторе (просто любопытно)? Я использую lua_setglobal, потому что считаю, что это правильный способ регистрации модуля в 5.3, поскольку register и lua_register устарели в 5.2. Как модуль должен быть зарегистрирован нормально?   -  person m_vdbeek    schedule 06.04.2018
comment
lua_open и lua_register*   -  person m_vdbeek    schedule 06.04.2018
comment
Что касается местных жителей в интерактивном интерпретаторе, см. этот ответ.   -  person siffiejoe    schedule 06.04.2018
comment
Что касается модулей: принято больше не устанавливать глобальные переменные в модулях Lua, а использовать возвращаемое значение require (и, следовательно, функции luaopen_*). Таким образом, lua_newtable + luaL_setfuncs подходит, как и использование luaL_newlib (что примерно эквивалентно приведенной выше комбинации). Но если вы хотите установить глобальный и вернуть таблицу модулей из функции luaopen_*, вы должны сначала продублировать ее в стеке.   -  person siffiejoe    schedule 06.04.2018


Ответы (2)


luaL_setfuncs просто регистрирует ваши функции в таблице.

Вместо этого используйте luaL_newlib. Он создает новую таблицу и регистрирует там ваши функции. Затем вам нужно поместить таблицу в стек lua.

luaL_newlib (L, foo);
return 1;
person Ravi    schedule 06.04.2018

Что касается причины ошибки, то lua_setglobal извлекает библиотечную таблицу из стека, так что luaopen_foo не возвращает ее. Тогда (хотя я не понимаю, почему) require "foo" вместо этого возвращает путь к файлу библиотеки (строка), а поле ("foo_filepath").bar равно nil.

Таким образом, удаление вызова lua_setglobal устраняет проблему, даже если вы не используете макрос создания библиотеки luaL_newlib, как рекомендует Рави.

Чтобы установить библиотечную таблицу как глобальную foo и вернуть ее из require, вы должны поместить вторую копию таблицы в стек с помощью lua_pushvalue(L, -1) перед выполнением lua_setglobal(L, "foo"), оставив исходную копию таблицы для возврата от luaopen_foo.

person cyclaminist    schedule 06.04.2018
comment
Цитата из require ручного ввода: Как только загрузчик найден , require вызывает загрузчик с двумя аргументами: имя мода и дополнительное значение, зависящее от того, как он получил загрузчик. (Если загрузчик получен из файла, это дополнительное значение является именем файла.). Итак, функция luaopen_* начинается с этих двух значений в стеке. После извлечения таблицы модуля lua_setglobal имя файла становится самым верхним элементом, оставшимся в стеке. - person siffiejoe; 07.04.2018