lightuser data in Lua

Started by
5 comments, last by Laval B 10 years, 6 months ago

Hello everyone.

An application we are working on needs some parts to be scripted. The scripts will only act as callbacks to controle some part of the execution of some transactions. To do so, it must expose a few objects from the host application. The lifetime of these objects is well controled by the host and they will outlive the execution of the script. The logical choice seems to be the use of lightuser data.

We have exposed these objects using functions and lightuser data and it does work. However, lightuser data don't have their individual metatable. Instead, they all share the same so they all share the same metamethods. It would be great though if we could associate specific methods to each object exposed (like it can be done with full user data) so it would be possible to use object oriented programming notations on those objects from the script.

At the moment, we have to use a notation like this :



function f(user, logger)

   setName(user, "name")
   setGroup(user, "group")

   output(logger, "User is set.")

end

instead of


function f(user, logger)

   user:setName("name")
   user:setGroup("group")

   logger:output("User is set.")

end

Is there a way to do this with lightuser data ?

We think in generalities, but we live in details.
- Alfred North Whitehead
Advertisement
I'm not an expert of lua myself, but you can create tables called User and Logger (or something else) containing the methods you need and then write something like the following in your script:


function f(user, logger)

   User.setName(user, "name")
   User.setGroup(user, "group")

   Logger.output(logger, "User is set.")

end
This isn't really what you were asking, but I think the only way to associate methods to data exported to lua is to use full user data. You can't do it using lightdata.

tbl.func(tbl, "parameter")

is the same as

tbl:func("parameter")

I don't know how this works on the C side, but in Lua, you'd declare and use a class like this:

local User = { };

function User.new()
    return setmetatable({ }, {__index = User});
end

function User:setName(name) -- Same as function User.setName(self, name)
    self.name = name
end


local user = User.new();
user:setName("John");

local user2 = User.new();
user2:setName("Jane");

print(user.name, user2.name); -- John, Jane

However I don't know if this helps you.

Falling block colour flood game thing I'm making: http://jsfiddle/dr01d3k4/JHnCV/


Is there a way to do this with lightuser data ?
Pretty sure, no. I ended up using the syntax that apatriarca suggests. Here's another example of the same syntax being used due to the choice to use light userdata:

http://bitsquid.blogspot.com.au/2011/06/lightweight-lua-bindings.html


Is there a way to do this with lightuser data ?
Pretty sure, no. I ended up using the syntax that apatriarca suggests. Here's another example of the same syntax being used due to the choice to use light userdata:

http://bitsquid.blogspot.com.au/2011/06/lightweight-lua-bindings.html

Thank you, i was pretty sure it wasn't possible, i asked just in case. The method suggested by apatriarca is what we are using, the functions are exposed via a call to luaL_newlib and lua_setglobal so they appear in a "namespace" or library.

We think in generalities, but we live in details.
- Alfred North Whitehead


Is there a way to do this with lightuser data ?
Pretty sure, no. I ended up using the syntax that apatriarca suggests. Here's another example of the same syntax being used due to the choice to use light userdata:

http://bitsquid.blogspot.com.au/2011/06/lightweight-lua-bindings.html

With just light user data, no, but if you're not concerned about allocating a full userdata as a wrapper (edit: you're not, I do this in my games and if you ever get to the point where it's actually a performance problem, you're doing way too much in script), I like to embed the light user data in a normal user data. Any functions called in the user data can then pull the light user data out in native code and work on it.

This should be exactly what you are looking for, as you can use the hybrid user data just like a normal lua table as per your second code block in the original post. You can override values, call functions with colon syntax, and only the wrapper will be under lua's memory management. The object pointed to by the wrapper will still be safe after the wrapper is destroyed.

Example:


// to create the lua binding object (in C)
    // push an empty table. This will be the object in lua we will "return" to lua
    lua_newtable(L);

    // put the C object in the "privatecobject" field of the lua object as a light user data
    lua_pushlightuserdata(L, user);
    lua_setfield(L, -1, "privatecobject");

    // do your normal metatable setting here

// then setName() in C would look like this (using your example function)
// this would be called from lua as user:setName("name")
int setName(lua_state* L)
{
    // grab the wrapper from the stack
    lua_getfield(L, 1, "privatecobject");
    User* user = static_cast<User*>(lua_touserdata(L, -1));
    lua_pop(L, 1); // pop the userdata field so the stack is unchanged. Not really needed in this function, but a good habit

    user->setName(lua_tostring(L, 2));
    return 0;
}

I like to embed the light user data in a normal user data. Any functions called in the user data can then pull the light user data out in native code and work on it.

That's a very good idea. When i looked at it, i was thinking : why didn't i think about that mellow.png . Thanks alot for sharing.

We think in generalities, but we live in details.
- Alfred North Whitehead

This topic is closed to new replies.

Advertisement