Hellagot
Hellagot

Reputation: 235

how to create lua c++ wrapper from c++

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

Answers (2)

Alex
Alex

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

Cubic
Cubic

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

Related Questions