2016-07-02 5 views
1

Я в настоящее время могу связать классы C++ с Lua, завернутые в модули, которые я загружаю с помощью luaL_requiref с правильными статическими открытыми функциями, которые выполняют функции , и т.д. Прекрасно работает.Как зарегистрировать вложенный класс C++ в Lua

Но что, если я хотел связать вложенный класс?

Рассмотрим следующий C++ код:

class Foo { 
public: 
    Foo(){} 
    void do_something(); 

    class Bar { 
    public: 
     Bar(){} 
     void do_something_else(); 
    }; 
}; 

и регистрационная Lua:

int foo_new(lua_State* L) { 
    new(lua_newuserdata(L, sizeof(foo)))foo(); 
    luaL_setmetatable(L, "Foo"); 
    return 1; 
} 
int foo_do_something(lua_State* L) { 
    Foo* foo = (Foo*)luaL_checkudata(L, 1, "Foo"); 
    foo->do_something(); 
    return 0; 
} 
int luaopen_foo(lua_State* L) { 
    const luaL_Reg functions[] = { 
     {"__index", foo_new}, 
     {"do_something", foo_do_something}, 
     {nullptr, nullptr} 
    }; 
    if(luaL_newmetatable(L, "Foo")) { 
     luaL_setfuncs(L, functions, 0); 
     lua_pushvalue(L, -1); 
     lua_setfield(L, -2, "__index"); 
    } 
    return 1; 
} 

... 

luaL_requiref(L, "Foo", luaopen_foo, 1); 

я могу получить доступ Foo::do_something() в Lua, как, например:

foo = Foo() 
foo:do_something() 

теперь вопрос: Как зарегистрировать Foo::Bar вложенный класс в Lua, так что я могу получить доступ к нему, как, например:

bar = Foo.Bar() 
bar:do_something_else() 

По сути, я хочу, чтобы зарегистрировать Bar метод в Foo Метастабильного, а не глобально. Нужно ли мне позвонить по номеру luaL_requiref, или я могу сделать это в одном luaL_requiref?

Спасибо!

+1

Вы стали жертвой неспешной терминологии нескольких поколений. В то время как 'Bar' выглядит немного« sub », это вовсе не * подкласс *. Это * вложенный * класс. Мы предпочитаем технические термины «базовый класс», «производный класс» и «тип члена», которые не так-то просто понять. –

ответ

0

EDIT: Хорошо, теперь это совершенно другой вопрос.

Да, вы можете сделать это за один раз.

luaL_requiref функция просто вызывает переданную функцию с примочками, такими как проверка, если модуль это еще не загружен (и обновление package.loaded таблицу), регистрируя соответствующее глобальное значение, и т.д.

Я предполагаю, что вы дон 't хочу когда-либо загружать Bar отдельно от Foo или Foo без Bar, поэтому в одном номере "Foo" в package.loaded должно быть достаточно. Аналогично, нет никакой необходимости в глобальной переменной Bar.

Итак, просто положите его как поле своего Foo, и все.

P.S. Убедитесь, что вы когда-либо вызывали своих деструкторов: Обычно, если вы используете lua_newuserdata с новым размещением, вам необходимо использовать метаметод __gc.

EDIT2: Измените способ luaopen_Foo (примечание __call, а не __index для конструкторов.Лично я предпочитаю new для этой цели, но если вы хотите, чтобы создать их как local f = Foo(), то вам нужно __call):

int luaopen_foo(lua_State* L) 
{ 
    static const luaL_Reg functions[] = 
    { 
     {"__call"  , foo_new}, 
     {"do_something" , foo_do_something}, 
     {nullptr  , nullptr} 
    }; 

    if (luaL_newmetatable(L, "Foo")) 
    { 
     luaL_setfuncs(L, functions, 0); 
     lua_pushvalue(L, -1); 
     lua_setfield(L, -2, "__index"); 

     // ================= 
     // Here, your "Foo" metatable is on top of your stack 
     // about to be returned as the result of your luaopen_Foo. 
     // Simply put another table above and set if as a Foo.Bar 
     if (luaopen_FooBar(L)) 
      lua_setfield(L, -2, "Bar"); 
    } 
    return 1; 
} 

int luaopen_FooBar(lua_State * L) 
{ 
    static const luaL_Reg functions[] = 
    { 
     {"__call"   , foo_bar_new}, 
     {"do_something_else" , foo_bar_do_something_else}, 
     {nullptr    , nullptr} 
    }; 

    // luaL_newmetatable puts its result on top of the stack 
    // - exactly what we want for lua_setfield 
    if (luaL_newmetatable(L, "Foo::Bar")) 
    { 
     luaL_setfuncs(L, functions, 0); 
     lua_pushvalue(L, -1); 
     lua_setfield(L, -2, "__index"); 
    } 
    return 1; // Indicate the result is on top of the stack 
} 

Если вы были когда-нибудь интересно, то, что делает функцию luaL_requiref (с некоторыми псевдо-код):

void luaL_requiref(lua_State *L, const char *name, lua_CFunction openf, int set_global) 
{ 
    if (!try_get_already_loaded_module(modname)) 
    { 
     lua_pushcfunction(L, openf); // Call the openf function 
     lua_pushstring(L, modname); // with modname as its argument 
     lua_call(L, 1, 1); 

     memorize_the_result_for_future(modname); 
    } 
    if (set_global) 
    { 
     lua_pushvalue(L, -1);  // copy the module 
     lua_setglobal(L, modname); // set _G[modname] = module 
    } 
} 

Примечание разница: luaL_requiref вызывает вашу функцию из Lua, который обеспечивает надлежащую очистку стека после его выполнения, например, вы можете поместить там немного мусора, единственное, что вам нужно сделать, это то, что ваше значение верхнего уровня - это то, что вы хотите получить, вместе с return 1;. У вас нет такой роскоши, если вы вызываете эту функцию напрямую. Поэтому убедитесь, что добавляет только одно значение поверх стека.

+0

Хорошо, я признаю, что написал это довольно быстро - я удалил ключевые слова 'static'. Речь идет не о пользе инстанцирования _class_ в Lua. Речь идет об использовании 'luaL_requiref' для регистрации _class_. – Deathicon

+0

Отредактировано. Вам это нужно более подробно? – kikobyte

+0

Я настраиваю глобальный деструктор и т. Д., Просто хотел бы сократить код. Теперь, какие функции мне нужно вызвать, чтобы зарегистрировать «Бар» в качестве поля, как вы сказали? По какой-то причине я не могу заставить его работать. – Deathicon

Смежные вопросы