Reputation: 8639
I've got a scripting system working well using userdata objects. However, I now want to have a property on my userdata that can take a regular table.
I think what I should do is create a normal table and set the metatable to use my current set of metamethods, however I'm struggling to understand how to do this - I'm sure it's a simple tweak, I just can't see it right now.
My existing code looks like:
void
LuaContext::push(lua_State* state, boost::shared_ptr<LuaWrapped> wrapped) {
static struct luaL_Reg methods[] = {
{ "__index", LuaWrapped::static_get },
{ "__newindex", LuaWrapped::static_set },
{ "__len", LuaWrapped::static_len },
{ "__ipairs", LuaWrapped::static_ipairs },
{ "__pairs", LuaWrapped::static_pairs },
{ "__gc", LuaWrapped::static_gc },
{ "__eq", LuaWrapped::static_eq },
{ NULL, NULL }
};
LuaWrapped::Ptr **ptr = (LuaWrapped::Ptr **)lua_newuserdata(state, sizeof(LuaWrapped::Ptr *));
*ptr = new LuaWrapped::Ptr(wrapped);
if (luaL_newmetatable(state, "LuaWrapped")) {
lua_pushstring(state, "__index");
lua_pushvalue(state, -2);
lua_settable(state, -3);
luaL_openlib(state, NULL, methods, 0);
}
lua_setmetatable(state, -2);
}
The __gc
metamethod is in there to delete the LuaWrapped::Ptr
class (which is a wrapper to a boost::shared_ptr
). I guess I'll leave that along, and store the pointer in a lightuserdata field on the normal table.
Experimental custom metatable against normal table issue (per discussion in comments):
void
LuaContext::push(lua_State* state, boost::shared_ptr<LuaWrapped> wrapped) {
static struct luaL_Reg methods[] = {
{ "__index", LuaWrapped::static_get },
{ "__newindex", LuaWrapped::static_set },
{ "__len", LuaWrapped::static_len },
{ "__ipairs", LuaWrapped::static_ipairs },
{ "__pairs", LuaWrapped::static_pairs },
{ "__gc", LuaWrapped::static_gc },
{ "__eq", LuaWrapped::static_eq },
{ NULL, NULL }
};
lua_newtable(state);
LuaContext::push(state, "pointer");
lua_pushlightuserdata(state, new LuaWrapped::Ptr(wrapped));
lua_settable(state, -3);
lua_newtable(state);
luaL_openlib(state, NULL, methods, 0);
lua_setmetatable(state, -2);
}
int
LuaWrapped::static_get(lua_State* state) {
int argc = lua_gettop(state);
for (int i = 1; i <= argc; i++) {
const char *type = lua_typename(state, i);
std::cout << type << std::endl;
}
....
Expected output on a get:
table, string
Actual output on a get (Lua 5.2, Ubuntu 14.04):
boolean, userdata
Upvotes: 4
Views: 3965
Reputation: 81052
Storing arbitrary data along with a userdata is what userdata environments/uservalues are for.
The lua 5.2 method for doing this is to use the lua_setuservalue
and lua_getuservalue
functions to associate a table with the userdata. This table can then be used to store and retrieve arbitrary values related to the userdata.
In lua 5.1 the more general environment concept was used for this purpose through lua_setfenv
and lua_getfenv
but the idea is the same.
Upvotes: 5