Creating referenced table element - c

I created a more or less complex table in C. Now I want to create a reference on a lower level of the tree. Is this possible?
Idea:
ELEM000 +--> ELEM010
+--> ELEM020 +--> ELEM120
| +--> **ELEM121**
| +--> ELEM122
+--> ELEM030 +--> ELEM130
| +--> ELEM131
| +--> ELEM132
+--> **ELEM121**
The ELEM121 should also be visible one level above, i.e. be a reference
I added an example of what I wanted to to..
void PushL(lua_State *L, const char * str) {
char s[255];
strcpy(s, "ELEM"); strcat(s, str); lua_pushstring(L, s); // key
strcpy(s, "Value"); strcat(s, str); lua_pushstring(L, s); // value
lua_settable(L, -3);
}
void MakeTable( lua_State *L )
{
lua_pushstring(L, "TBL0"); // name of sub-table
lua_createtable(L, 0, 0);
lua_checkstack(L, 3);
{
PushL(L, "000");
lua_pushstring(L, "TBL1");
lua_createtable(L, 0, 0);
lua_checkstack(L, 3);
{
PushL(L, "010");
PushL(L, "020");
lua_pushstring(L, "TBL2");
lua_createtable(L, 0, 0);
lua_checkstack(L, 3);
{
PushL(L, "120");
PushL(L, "121");
PushL(L, "122");
lua_settable(L, -3);
}
PushL(L, "030");
lua_pushstring(L, "TBL3");
lua_createtable(L, 0, 0);
lua_checkstack(L, 3);
{
PushL(L, "130");
PushL(L, "131");
PushL(L, "132");
lua_settable(L, -3);
}
lua_settable(L, -3);
}
lua_pushstring(L, "ELEM121");
lua_pushstring(L, "SHOULD BE A REFERENCE TO ELEM121");
lua_settable(L, -3);
}
lua_setglobal(L,"____myTable");
}

Bottom line: In Lua, field variables cannot be referenced but there are ways of doing what you want.
Here is a comparison between C data structures and Lua data structures.
In C, you either:
have a copy of the value in two places
You can create copies in Lua, too.
or, have a pointer in one place that points to the other place.
In C, that means you'd have to access them differently, one with a pointer deference, the other without.
In Lua, you could have a function in one place that returns the value in the other place. That means you'd have to access them differently, one with a function call, the other without.
The following is equivalent to a read-only pointer:
local ELEM000 = {
ELEM010 = "ELEM010 value",
ELEM020 = {
ELEM120 = "ELEM120 value",
ELEM121 = "ELEM121 value",
ELEM122 = "ELEM122 value" },
ELEM030 = {
ELEM130 = "ELEM130 value",
ELEM131 = "ELEM131 value",
ELEM132 = "ELEM132 value" },
ELEM121 = function(self) return self.ELEM020.ELEM121 end }
print(ELEM000.ELEM020.ELEM121)
print(ELEM000:ELEM121())
ELEM000.ELEM020.ELEM121 = ELEM000.ELEM020.ELEM121 .. " updated"
print(ELEM000.ELEM020.ELEM121)
print(ELEM000:ELEM121())
If you need a writable pointer then another approach would be needed.
Update
A simple way for a writable pointer is to add an optional value parameter. This is commonly used in JavaScript APIs but JavaScript has the advantage of the undefined data type. In Lua, we'll have to use nil, which means that you can't write nil as a value.
ELEM121 = function(self, value)
if (value ~= nil) then self.ELEM020.ELEM121 = value end
return self.ELEM020.ELEM121
end
For true read-write field access, use the __index and __newindex metatmethods. This requires that the field actually not have a key in the table. The metamethods are invoked when indexing a non-existing field for reading (__index) or for writing (__newindex).
local ELEM000 = {
ELEM010 = "ELEM010 value",
ELEM020 = {
ELEM120 = "ELEM120 value",
ELEM121 = "ELEM121 value", -- captured as the initial value
ELEM122 = "ELEM122 value"},
ELEM030 = {
ELEM130 = "ELEM130 value",
ELEM131 = "ELEM131 value",
ELEM132 = "ELEM132 value" },
ELEM121 = nil -- ignored
}
setmetatable(ELEM000, {
__index = function(base, key)
if (key=="ELEM121") then return base.ELEM020.ELEM121
else return nil end end,
__newindex = function (base, key, value)
if (key=="ELEM121") then base.ELEM020.ELEM121 = value
else rawset(base, key, value) end end })
setmetatable(ELEM000.ELEM020, {
ELEM121 = ELEM000.ELEM020.ELEM121, --[[ backing storage for field,
initialized to existing value]]
__index = function(base, key)
if (key=="ELEM121") then return getmetatable(base).ELEM121
else return nil end end,
__newindex = function (base, key, value)
if (key=="ELEM121") then getmetatable(base).ELEM121 = value
else rawset(base, key, value) end end })
-- make sure metamethods will be invoked on these fields
rawset(ELEM000, "ELEM121", nil)
rawset(ELEM000.ELEM020, "ELEM121", nil)
Usage:
print(ELEM000.ELEM020.ELEM121)
print(ELEM000.ELEM121)
ELEM000.ELEM020.ELEM121 = ELEM000.ELEM020.ELEM121 .. " updated"
print(ELEM000.ELEM020.ELEM121)
print(ELEM000.ELEM121)
ELEM000.ELEM121 = ELEM000.ELEM121 .. " again"
print(ELEM000.ELEM020.ELEM121)
print(ELEM000.ELEM121)
There various places to store the backing field and different styles of coding the metamethods. This code is perhaps too concise. And, I leave it to you to code it in Lua C API, if you wish.

Related

How to get metatable set by lua from Lua C API

Lua:
a = {
b = "c",
d = {
e = "f",
g = "h"
}
}
setmetatable(a.d, {__ismt = true})
cfun(a) --call C function to iterate over table a
C:
int cfun(lua_State *L)
{
lua_pushnil(L);
while (lua_next(L, -2) != 0)
{
// iterate over table
lua_pop(L, 1);
}
}
How do you know if there is a metatable when the host client iterates over the table? And then how do you get the metatable?
The table is in a form of a tree, and you need to traverse the tree in iterative mode. Lua already has a stack implementation, so this makes the job easier.
On entrance, the stack has the table at top, you will push a nil element since lua_next() will consume one element from the stack before checking the table. So the stack will look like table -> nil.
Next, we are calling lua_next() which will consume one element from the stack and will add two new key-value pair from the table. The stack will look like table -> key -> value. If there is no next element, the return value of the call is 0.
If the return value is 1, and the value on stack is a nested table, you will push nil on the stack, so now the stack will look like table -> key -> table -> nil. Now you are almost like on the begining, so with the loop, you will start traversing the nested table.
if the return value is 1, and if the value is not table, to your stuff with the value
if the return value is 0, we can check if this was a meta table. After the check, you will pop the value and check if the stack is table -> key or any -> key. If the second element on the stack is not a table, you have finished the traversing and you will break the loop.
Here is the C code that implements the algorithm. I have left the printf in order to help debugging. The printf() should be removed.
static int cfun(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
lua_pushnil(L); // Add extra space for the first lua_next to pop
int loop=1;
do {
if ( lua_next(L,-2) != 0 ) {
if (lua_istable(L,-1)) {
printf("Table [%s] \n", lua_tostring(L, -2));
lua_pushnil(L); // Start iterating this sub-table
} else {
// The Key and Value are on the stack. We can get their type
printf("(%s - %s)\n",
lua_tostring(L, -2),
lua_typename(L, lua_type(L, -1)));
lua_pop(L,1);
}
} else {
printf("table finished, still on stack (%s -> %s -> %s)\n",
lua_typename(L, lua_type(L, -3)),
lua_typename(L, lua_type(L, -2)),
lua_typename(L, lua_type(L, -1)));
if (lua_getmetatable(L,-1)) {
// The table has metatable. Now the metatable is on stack
printf("Metatable detected\n");
lua_pop(L,1); // remove the metatable from stack
}
lua_pop(L,1); // Pop the current table from stack
if (!lua_istable(L, -2)) {
loop = 0; // No more tables on stack, breaking the loop
}
}
} while (loop);
lua_pop(L,1); // Clear the last element
lua_pushnumber(L,0); // Return 0
return 1;
}

Lua userdata array access and methods

I am writing in C a userdata type for use in Lua. It has some array-type properties and various methods aswell. Right now if u is of this type, I use u:set(k,v) resp. u:get(k) to access data and e.g. u:sort() as method. For this I set __index to a table containing these methods. Now if I want to access the data using u[k] = v or u[k], I need to set __newindex and __index to set resp get. But then the other methods are no longer accessible...
What's the best way to deal with this in C? I am guessing I need to write a function in C to register as __index and somehow deal with it there. Maybe check if key belongs to a Lua table of methods and if so call it.
Any help/hints would be appreciated. I did not find examples like this, although it seems a very natural thing to do (to me.)
edit: Added my C version of the solution in Lua posted in the answer below. This is more or less a direct translation, so all credit goes to #gilles-gregoire .
The following C function is registered as __index metamethod.
static int permL_index(lua_State *L) {
struct perm **pp = luaL_checkudata(L, 1, PERM_MT);
int i;
luaL_getmetatable(L, PERM_MT);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if ( lua_isnil(L, -1) ) {
/* found no method, so get value from userdata. */
i = luaL_checkint(L, 2);
luaL_argcheck(L, 1 <= i && i <= (*pp)->n, 2, "index out of range");
lua_pushinteger(L, (*pp)->v[i-1]);
};
return 1;
};
This is the code that does that,
int luaopen_perm(lua_State *L) {
luaL_newmetatable(L, PERM_MT);
luaL_setfuncs(L, permL_methods, 0);
luaL_setfuncs(L, permL_functions, 0);
lua_pop(L, 1);
luaL_newlib(L, permL_functions);
return 1;
};
where permL_methods is
static const struct luaL_Reg permL_methods[] = {
{ "__index", permL_index },
{ "__eq", permL_equal },
{ "__tostring", permL_tostring },
{ "__gc", permL_destroy },
[...]
{ NULL, NULL }
};
and permL_functions is
static const struct luaL_Reg permL_functions[] = {
{ "inverse", permL_new_inverse },
{ "product", permL_new_product },
{ "composition", permL_new_composition },
[...]
{ NULL, NULL }
};
This looks like a problem which can be solved with nested metatables. You need one metatable for the methods (like your sort() method), and a second one for index operations. That second metatable is actually the metatable of the methods metatable.
Let me write this as lua code. You need 3 tables:
-- the userdata object. I'm using a table here,
-- but it will work the same with a C userdata
u = {}
-- the "methods" metatable:
mt = {sort = function() print('sorting...') end}
-- the "operators" metatable:
op_mt = {__index = function() print('get') end}
Now, the tricky part is here: lua will first lookup u when you will call a method.
If it does not find it, it will lookup in the table pointed by the __index field of u's metatable... And Lua will repeat the process for that table!
-- first level metatable
mt.__index = mt
setmetatable(u, mt)
-- second level metatable
setmetatable(mt, op_mt)
You can now use your u like this:
> u:sort()
sorting...
> = u[1]
get
nil
EDIT: a better solution by using a function for the __index metamethod
Using a function for the __index metamethod is probably the right way to this:
u = {}
mt = {sort = function() print('sorting...') end}
setmetatable(u, mt)
mt.__index = function(t, key)
-- use rawget to avoid recursion
local mt_val = rawget(mt, key)
if mt_val ~=nil then
return mt_val
else
print('this is a get on object', t)
end
end
Usage:
> print(u)
table: 0x7fb1eb601c30
> u:sort()
sorting...
> = u[1]
this is a get on object table: 0x7fb1eb601c30
nil
>

Lua C API register fallback __index

I would like to know how I can write something like this Lua Snippet from http://lua-users.org/wiki/MetamethodsTutorial
local func_example = setmetatable({}, {__index = function (t, k)
return "key doesn't exist"
end})
local fallback_tbl = setmetatable({
foo = "bar",
[123] = 456,
}, {__index=func_example})
local fallback_example = setmetatable({}, {__index=fallback_tbl})
print(func_example[1]) --> key doesn't exist
print(fallback_example.foo) --> bar
print(fallback_example[123]) --> 456
print(fallback_example[456]) --> key doesn't exist
in the lua C api. I.e. I want lua to first check whether a key is in a members metatable and otherwise call the __index implementation. I have come up with something like this:
static const struct luaL_reg
lobj_fallback[] = {
{"__index", lobject_index},
{"__newindex", lobject_newindex},
{"__tostring", lobject_tostring},
{NULL, NULL},
};
static const struct luaL_reg
lobj_members[] = {
{"delete", lobject_delete},
{NULL, NULL}
};
{
// ....
luaL_newmetatable(L, "MyMetaTable");
luaL_register(L, NULL, lobj_members);
luaL_newmetatable(L, "MyMetaTableFallback");
luaL_register(L, NULL, lobj_fallback);
lua_setmetatable(L, -2);
// ...
}
Yet this does not work as expected, processing the fallback __index works but not the members metatable ("attempt to call method 'delete' (a nil value)").
If your intention is to fallback to the indexing function when accessing "MyMetaTable", then you have swapped the names of your table and metatable. Thus the members table (lobj_members) becomes called "MyMetaTableFallback" and the metatable (lobj_fallback) becomes called "MyMetaTable".

How to access multidimensional table from lua in C?

Hello I am really stumped with this seemingly simple task.
I can access the properties of a table passed to a function in C, but cannot access the members of any subtable i create in it.
Basically I want to simply be able to extract the strings from the properties table so i can create say a "wheel" according to the users expectations.
Here is what I have so far (tried so much my brain is fried)
Lua Side:
--Function
createSomething( "wheel", { canInflate = true, properties = { "large", "full" } } )
C Side:
//I can retrieve any value easily within that table, but cannot seem to extract the table
//Within it named "properties", i can access the table, but cannot extract the strings inside
if( lua_istable(L, 2) ) {
lua_getfield(L, 2, "canInflate"); // Let's extract the value for the key 'someKey'. Pushes the value on the top of the stack
static int canInflate = lua_toboolean(L, -1); // get the value of bool now at the top of stack (index: -1)
//printf("can inflate is %d\n", canInflate);
//lua_pop(L, 1); // pop the value now that we are done with it
}
//try to get the properties table
if ( lua_istable(L, 2) ) {
lua_getfield(L, 2, "properties");
const char *str = lua_tostring(L, -1);
printf( "properties 1 = %s\n", str); // NULL
lua_pop(L, 2);
}
Any help on this would be greatly appreciated
The problem you're having is with how you specify tables in Lua: the following 3 statements have exactly the same result:
t = { 'full','large'}
t = { [1] = 'full', [2] = 'large'}
t={};t[1]='full';t[2]='large'
What you want is to use the strings as keys instead of values (as is done in your code and the above samples):
t={full=true,large=true}
-- or
t={}; t.full=true; t.large=true
If you use the strings as keys your C code should work.

Is there something wrong with how I'm referencing my instances in this C extension?

I'm having some issues where when if I run this C extension outside of a Rails environment it works, but when I run inside Rails it gives me a stack dump.
I get this error message:
NoMethodError Exception: undefined method `evaluate' for #<String:0x00000103557db0>
This is presumably referring to the calls I am making within the EV::Counters evaluate function, to the "evaluate" functions that exist in the three instances that I am calling.
Strangely valgrind is not giving me any errors. But I think there is something basic I might be doing wrong with how I reference my instances?
VALUE rFlushInstance, rPairCounterInstance, rStraightInstance;
static VALUE
evaluate(VALUE self, VALUE val, VALUE suit, VALUE index)
{
rb_funcall(rFlushInstance, rb_intern("evaluate"), 3, val, suit, index);
rb_funcall(rStraightInstance, rb_intern("evaluate"), 2, val, index);
rb_funcall(rPairCounterInstance, rb_intern("evaluate"), 2, val, index);
return Qnil;
}
VALUE EV;
void Init_counters()
{
EV = rb_define_module("EV");
VALUE Counters = rb_define_class_under(EV, "Counters", rb_cObject);
init_pair_counter();
init_straight();
init_flush();
VALUE Flush = rb_const_get(EV, rb_intern("Flush"));
VALUE PairCounter = rb_const_get(EV, rb_intern("PairCounter"));
VALUE Straight = rb_const_get(EV, rb_intern("Straight"));
rFlushInstance = rb_class_new_instance(0, NULL, Flush);
rStraightInstance = rb_class_new_instance(0, NULL, Straight);
rPairCounterInstance = rb_class_new_instance(0, NULL, PairCounter);
rb_define_method(Counters, "initialize", initialize_counters, 2);
rb_define_method(Counters, "evaluate", evaluate, 3);
}
What I needed to do was to store the instances as instance variables, like:
VALUE rPairCounterInstance = rb_class_new_instance(0, NULL, PairCounter);
rb_ivar_set(self, rb_intern("#pair"), rPairCounterInstance);

Resources