2013-10-02 2 views
3

У меня есть класс, который я использую в своем коде на C++ и в некоторых сценариях Lua. Соответствующая часть класса выглядит следующим образом:C++ & Lua Api, размещение на месте с заводской функцией

typedef boost::shared_ptr<Thing> ThingPtr; // convenient 

class Thing 
{ 
public: 
    Thing() { /* do some stuff */ } 
    ~virtual Thing() { } 

    ThingPtr createThing() 
    { 
     ThingPtr thing(new Thing); 

     // initialization can't be done in constructor 
     thing->doSomeInit(); 

     return thing; 
    }  

// other stuff.... 

}; 

Я разоблачить этот класс в Lua (без привязки или что-нибудь «фантазии»). Прежде чем я добавил функцию фабрики, моя функция Lua, чтобы создать Thing выглядел как:

int MyLua::newThing(lua_State* L) 
{ 
    int size = sizeof(Thing); 

    // allocate a new Thing object in place 
    new ((Thing*)lua_newuserdata(L, size)) Thing(); 

    luaL_setmetatable(L, "MyStuff.thing"); 

    return 1; 
} 

После того, как я добавил функцию заводскую я что-то вроде:

int MyLua::newThing(lua_State* L) 
{ 
    int size = sizeof(Thing); 

    // allocate a new Thing object in place 
    Thing* thing = new ((Thing*)lua_newuserdata(L, size)) Thing(); 
    thing->doSomeInit(); 

    luaL_setmetatable(L, "MyStuff.thing"); 

    return 1; 
} 

Это прекрасно, казалось хорошо, за исключением что теперь я хочу, чтобы конструктор Thing был закрыт для обеспечения использования фабричной функции в других местах кода C++. Итак, теперь у меня есть что-то вроде:

int MyLua::newThing(lua_State* L) 
{ 
    int size = sizeof(Thing); 
    ThingPtr thing = Thing::createThing(); 

    void* space = lua_newuserdata(L, size); 
    memcpy(space, client.get(), size); 

    luaL_setmetatable(L, "MyStuff.thing"); 

    return 1; 
} 

Мой вопрос: есть ли лучший способ сделать это? Призыв к memcpy заставляет меня чувствовать себя некомфортно.

+0

Несвязанное и педантичное примечание: вы не должны передавать статическую функцию-член как 'lua_CFunction', потому что статическая функция-член никогда не имеет связи на языке Си. Большинство компиляторов не диагностируют это, но стандарт говорит, что это запрещено. – Simple

+0

не используют ничего интересного, просто используйте привязку, например [LuaBridge] (https://github.com/vinniefalco/LuaBridge), руководство которой объясняет все трудности управления жизненным циклом объекта и предлагает решения. –

ответ

1

Вы не можете передать права собственности на объекты C++ на Lua.

Ваш исходный код является ошибочным, потому что он никогда не вызовет деструктор для вашего Thing s. Хотя Lua будет мусором собирать память, выделенную через lua_newuserdata, она не будет вызывать деструктор объекта (просто потому, что Lua, будучи библиотекой C, не знает о концепции деструкторов).

Таким образом, вам понадобится отдельная конструкция на стороне C++, которая управляет временем жизни ваших объектов и передает только необработанные указатели на Lua для отображения в качестве пользовательских данных.

+0

Я считаю, что Lua «владеет» объектом в исходном коде, так как я использую пространство Lua, предоставленное в его распределении. Я также обработал очистку своей собственной функцией __gc, которая удаляет объект. – Addy

+1

@Addy Хотя это работает теоретически, я бы посоветовал это сделать, поскольку гарантии Lua на вызов финализаторов намного слабее, чем гарантии C++ для вызова деструкторов.Я бы предпочел, чтобы вызов '__gc' просто сигнализировал код C++, чтобы освободить объект. Риск случайного попадания в неопределенное поведение из-за испорченных финализаторов слишком велик, чтобы оправдать небольшой выигрыш комфорта здесь imho. – ComicSansMS

+1

Я не понимаю, что вы получаете, просто сигнализируя C++ о выпуске объекта. Если вы полагаетесь на Lua для вызова __gc, чтобы сигнализировать C++, что объект готов к выпуску, почему бы не избавиться от промежуточного шага и просто отпустить объект в вызове __gc? – Addy

1

Это должно сделать вас неудобным; memcpy допускается только для типов с возможностью копирования (Thing не такой тип). Я даже не уверен, что new (lua_newuserdata(L, size)) Thing() разрешен, потому что Lua использует realloc, чтобы требовать новую память по умолчанию, и это может привести к перемещению памяти (т. Е. realloc может memcpy это так или иначе).

Решением, ИМО, является динамически выделять ваш Thing (который, кажется, ваш createThing завода действительно сделать, но с помощью смарт-указателя) и хранить указатель C на объект в пользовательских данных с __gc Метаметодом, которая очищает ваш объект , С умным указателем это сложнее, но это потребует хранения копии умного указателя в куче, сохранения указателя C на интеллектуальном указателе в пользовательских данных, а затем освобождения умного указателя в метатете __gc.

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