Reputation: 235
I have some lua 'objects' that are wrappers to C++ objects, they hold a local reference to the c++ object and call it.
Now i want some functions in C++ return those wrappers, so i need to call this lua function and then set the C++ object on it.
I experience crashes and i suspect that i am not handling the lua stack right. For example, if i ask for lua_top before exiting a function that creates the wrapper + c++ object, i get 5 as result, shouldn't it be 1 if i return 1 object?
So here is what i do, maybe i am doing it wrong, maybe there is a better way to do this.
c++, .h:
#define gLuaGet(L, var, type) \
if (lua_istable(L, 1)) {\
lua_getfield(L, 1, "CObj");\
lua_replace(L, 1);\
}\
type& var = *(type*)lua_touserdata(L, 1);
#define gLuaCreate(L, type) new (lua_newuserdata(L, sizeof(type))) type();
class MyObject {
public:
MyObject();
int somefunc();
};
int MyObjectCreate(lua_State *L);
int MyObjectCallSomefunc(lua_State *L);
c++, .cpp:
int MyObject::somefunc() {
std::cerr << "in c++ function" << std::endl;
return 123;
}
int MyObjectCreate(lua_State *L) {
gLuaCreate(L, MyObject);
return 1;
}
int MyObjectCallSomefunc(lua_State *L) {
gLuaGet(L, obj, MyObject);
int r = obj.somefunc();
lua_checkstack(L, 1);
lua_pushnumber(L, r);
return 1;
}
lua wrapper:
function MyObject(donotinit)
self = {}
self.CObj = nil
if (donotinit == nil) then
self.CObj = MyObjectCreate()
end
self.setCObject = function(obj)
self.CObj = obj
end
self.somefunc = function()
return MyObjectCallSomeFunc(self)
end
return self
end
Now i want some other wrapper to return a MyObject that is created within c++, so here is the c++ code that is called from the new wrapper (for better readabiliy i removed the sanity checks on lua_pcall):
int returnLuaMyObject(lua_State *L) {
gLuaGet(L, obj, MyOtherObject);
MyObject *myObject = obj.getMyObject(); // get c++ part
lua_getglobal(L, "MyObject"); // create lua part
lua_pushnumber(L, 1); // and tell it not to initialize the self.CObj
lua_pcall(L, 1, 1, 0);
lua_getfield(L, -1, "setCObject"); // call the setCObject function
lua_pushlightuserdata(L, myObject); // give c++ object as param
lua_pcall(L, 1, 0, 0);
// at this point lua_gettop(L); returns 5, can this be correct?
return 1;
}
Well, if i call this function via a lua wrapper now a couple of times, everything seems fine, but if i call it lets say 50 times in a while loop, its crashing on random times (but always on the same c++ line)
Am i doing something wrong here? Is it OK for the lua stack top to be 5 at this point, where it only returns one object?
Upvotes: 2
Views: 1873
Reputation: 15333
This is what your Lua stack will look like after each function/macro call, if I'm reading this correctly
int returnLuaMyObject(lua_State *L) {
// arg
gLuaGet(L, obj, MyOtherObject); // arg.CObj
MyObject *myObject = obj.getMyObject();
lua_getglobal(L, "MyObject"); // arg.CObj _G.MyObject
lua_pushnumber(L, 1); // arg.CObj _G.MyObject 1
lua_pcall(L, 1, 1, 0); // arg.CObj obj
lua_getfield(L, -1, "setCObject"); // arg.CObj obj obj.setCObject
lua_pushlightuserdata(L, myObject); // arg.CObj obj obj.setCObject myObject
lua_pcall(L, 1, 0, 0); // arg.CObj obj
// at this point lua_gettop(L); returns 2.
// If you end this function with return 1, only obj is returned
// to Lua, everything else is discarded.
return 1;
}
(For what it's worth, when writing Lua code I religiously put comments like that on every single line that messes with the Lua stack so I always know what I'm operating on. Once you memorize the side effects of Lua function it makes bug finding very easy)
This should be fine assuming that returnLuaMyObject is being called from Lua. If you're calling it in a loop in C++ your stack is going to get messed up since it's left with two things on the stack and some of your functions are hardcoded to operate on index 1.
A better way to do this is to do what Cubic suggested and use some templates rather than macros. You should also avoid using hardcoded indexes when possible so that you can reuse your functions in situations where the object you're interested in is in a difference spot on the stack. For example, your gLuaGet
should take an index as an argument so that you can use it anywhere. (I'd also get rid of the obj
argument and drop the entire last line of the macro, it makes it unclear where the the variable obj
is being declared.
I wrote a library for myself (coincidentally called LuaWrapper, located here) which lets you do what you want without a lot of hassle. You can use luaW_to and luaW_push to push and get pointers from Lua just like they were numbers or strings. I'm just throwing it out there because I like it better than the suggestion of Luabind or toLua++
Upvotes: 2
Reputation: 15673
The top of the stack in Lua is -1, not 1 (or lua_gettop(state)
). Also, you should really use templates rather than macros for this. Or better yet, unless you have a reason not to, you could use luabind or tolua++. I'm currently writing something that works essentially the same as luabind, but uses C++11 features to drop the boost dependency, although it's nowhere near completion yet.
Upvotes: 0