У меня есть следующие классы в C ++
class B;
class A {
B* GetB();
void SetB(B*& b) { _b = b;};
private:
B* _b;
}
И часть кода привязки lua:
int A::setB(lua_State* L) {
A* a = checkA(L,1) // Macro for luaL_checkudata
B* b = checkB(L,2) // again similar macro
a->SetB(b);
return 0;
}
int A::getB(lua_State* L) {
A* a = checkA(L,1) // Macro for luaL_checkudata
B* b = a->GetB();
// how do i return the already created userdata for this B* instance?
// right now I am doing
B** bp = (B**)lua_newuserdata(L, sizeof(B*));
*bp = b;
luaL_getmetatable(L, "B");
lua_setmettable(L, -2);
return 1;
}
И я хочу обернуть их как пользовательские данные в Lua, чтобы я мог сделать что-то вроде:
local a = a.new() -- create new userdata
local b = b.new() -- create new userdata
a:SetB(b) -- associate B with A
local b2 = a:GetB() -- get the B associated with A back, and stored as b2
Когда я печатаю адреса b
а также b2
Я получаю два уникальных адреса, что имеет смысл, потому что я позвонил lua_newuserdata
, Но в идеале я хотел бы, чтобы он возвращал одни и те же пользовательские данные, поскольку они указывают на один и тот же блок памяти. Как можно это сделать?
Я хочу, чтобы Lua отвечала за память, чтобы она правильно удалялась при сборке мусора. Так что я не знаю, возможна ли легкая пользовательская информация.
Вы можете отразить переменные-члены ваших объектов C ++ в Lua, используя таблицу пользовательских значений. Таким образом, ваши методы становятся:
int A::setB(lua_State* L) {
A* a = checkA(L,1) // Macro for luaL_checkudata
B* b = checkB(L,2) // again similar macro
a->SetB(b);
// if you are paranoid about consistency, you should preallocate the
// the uservalue table slot for "_b", so that the following code
// can't fail during memory allocation
lua_getuservalue(L, 1); // use lua_getfenv in Lua 5.1
lua_pushvalue(L, 2); // duplicate B userdata on stack top
lua_setfield(L, -2, "_b"); // store it in the uservalue table
return 0;
}
int A::getB(lua_State* L) {
A* a = checkA(L,1) // Macro for luaL_checkudata
lua_getuservalue(L, 1);
lua_getfield(L, -1, "_b");
return 1;
}
Это имеет дополнительное преимущество, заключающееся в том, что пользовательские данные B не собираются (вызывая висячий указатель), пока на них ссылается объект A.
Убедитесь, что все объекты A на самом деле иметь таблица пользовательских значений с помощью lua_setuservalue()
(lua_setfenv()
для Lua 5.1) после каждого lua_newuserdata()
для объектов.
Вы также можете добавить проверки, чтобы убедиться, что сторона C ++ и сторона Lua все еще синхронизированы (в случае, если вы продолжаете использовать свой интерфейс и из C ++), но если вы обнаружите объект B, который не отражен в таблице пользовательских значений, это не так много, что вы можете сделать, кроме как поднять ошибку, потому что вы не знаете, как она туда попала и кому она принадлежит.
Редактировать:
Если вы хотите управлять контейнером указателей B, вы можете сделать это, сохранив все пользовательские данные в таблице (сама таблица пользовательских значений или поле в ней) и отразив все операции с контейнерами, но это может привести к ошибкам. В качестве альтернативы вы можете использовать таблицу пользовательских значений как отображение значения указателя C ++ (lightuserdata) на объект Lua (полные пользовательские данные). Это отображение сохранит пользовательские данные B, и вы можете просто искать их по значению указателя. Вам просто нужно обновлять отображение всякий раз, когда вы добавляете или удаляете объекты B. Например.
int A::pushB(lua_State* L) {
A* a = checkA(L,1)
B* b = checkB(L,2)
lua_getuservalue(L, 1);
lua_pushlightuserdata(L, (void*)b);
lua_pushvalue(L, 2);
lua_rawset(L, -3);
a->PushB(b);
return 0;
}
int A::popB(lua_State* L) {
A* a = checkA(L,1)
B* b = a->PopB();
lua_getuservalue(L, 1);
lua_pushlightuserdata(L, (void*)b);
lua_rawget(L, -2); // push full userdata
if (!a->ContainsB(b)) {
// remove reference only if this b isn't in the container anymore
// if done wrong b may be collected too soon (while A still has
// a pointer stored), or too late (when object a gets collected)
lua_pushlightuserdata(L, (void*)b);
lua_pushnil(L);
lua_rawset(L, -4);
}
return 1;
}
Других решений пока нет …