lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi,

in Lua 5.1-work3 the C API has been extended with the two very useful
functions luaL_getfield() and luaL_setfield(). These get/set a dot-separated
field from nested tables.

BTW: Do not confuse them with lua_getfield() and lua_setfield()
     (without the 'luaL_' -- part of the core API and not of lauxlib).

I think there is a generic need for this functionality on the Lua side, too
(this is one of the standard questions here on the list). Since we already
got the code it seems a waste not to provide Lua functions for it.


See the appended patch for two new Lua functions:

table.getfield(t, name)         -- returns the value of the field (or nil)
table.setfield(t, name [, val]) -- sets the field to the value (or deletes it)

table.setfield() creates intervening tables if required. However it does not
delete intervening tables if a field is deleted.

table.setfield() only throws an error for name conflicts (intervening field
element is not a table).

Both functions use rawget/rawset internally and thus are not suited for
anything but 'plain' tables (without __index/__newindex metamethods).
They are not too useful for tables that override these methods, anyway.

Note that all field elements are interpreted as string indexes. E.g.
table.getfield(t, "1") is accessing t["1"] and not t[1]. You'll get
interesting results when passing a non-integer number as the field name. :-)

A starting or trailing dot or double dots will use the empty string as
an index. This may or may not be what you want, so check for this condition.


Trivial example:

print(table.getfield(_G, "foo"))          --> nil
print(table.getfield(_G, "foo.bar"))      --> nil
print(table.getfield(_G, "foo.bar.baz"))  --> nil
print(foo.bar.baz)                        --> (throws an error)

table.setfield(_G, "foo.bar.baz", "test")
print(table.getfield(_G, "foo"))          --> table: 0x806ec40
print(table.getfield(_G, "foo.bar"))      --> table: 0x806ed18
print(table.getfield(_G, "foo.bar.baz"))  --> "test"
print(foo.bar.baz)                        --> "test"

table.setfield(_G, "foo.bar.baz")
print(table.getfield(_G, "foo"))          --> table: 0x806ec40
print(table.getfield(_G, "foo.bar"))      --> table: 0x806ed18
print(table.getfield(_G, "foo.bar.baz"))  --> nil
print(foo.bar.baz)                        --> nil


BTW: I assume there are historical reasons why the C functions in ltablib.c
     are prefixed with luaB_ (which belongs to lbaselib.c)?

Bye,
     Mike
--- lua-5.1-work3/src/lib/ltablib.c	2004-06-15 15:37:21.000000000 +0200
+++ lua-5.1-work3-table.getfield/src/lib/ltablib.c	2004-12-07 09:45:36.241791000 +0100
@@ -106,6 +106,24 @@
 }
 
 
+static int luaB_getfield (lua_State *L) {
+  const char *name = luaL_checkstring(L, 2);
+  luaL_checktype(L, 1, LUA_TTABLE);
+  if (luaL_getfield(L, 1, name)) lua_pushnil(L);
+  return 1;
+}
+
+
+static int luaB_setfield (lua_State *L) {
+  const char *name = luaL_checkstring(L, 2);
+  luaL_checktype(L, 1, LUA_TTABLE);
+  lua_settop(L, 3);
+  if (luaL_setfield(L, 1, name))
+    luaL_error(L, "name conflict setting field `%s'", name);
+  return 0;
+}
+
+
 static int str_concat (lua_State *L) {
   luaL_Buffer b;
   size_t lsep;
@@ -242,6 +260,8 @@
   {"sort", luaB_sort},
   {"insert", luaB_tinsert},
   {"remove", luaB_tremove},
+  {"getfield", luaB_getfield},
+  {"setfield", luaB_setfield},
   {NULL, NULL}
 };