lua-users home
lua-l archive

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

Thanks for the info!
On Nov 13, 2006, at 4:28 PM, Karel Tuma wrote:


i hate table proxying too.

for mirroring C structures it's some serious pain in the ass, not to
mention performance.

take a look at this

(original idea by Christopher Dunn)

__usedindex get called the same way as __newindex when there
actually _is_ existing key in the table. also allows to set new table
to redirect new writes there.

note that exactly same functionality is achieavable using proxy tables
but with 3x more code with little to none readability.

i'll better put it on powerpatches page to avoid people writing the
patch over and over again :)

ps: can someone using luajit figure out why this patch does not work
there, please? (probably something to do with fasttm's)
$ cat redir.lua
redir = {}
tab = { a = 1, b = 2 }
setmetatable(tab, { __newindex = redir, __usedindex = redir })
setmetatable(redir, { __usedindex = function(_,k)
	error("attempted to overwrite "..k)
end })
tab.a = 3
tab.c = 4
for k,v in pairs(tab) do
for k,v in pairs(redir) do
tab.c = 5

$ lua redir.lua
a       1
b       2
a       3
c       4
lua: redir.lua:4: attempted to overwrite c

On Mon, Nov 13, 2006 at 04:41:35PM +0100, Michael Grubb wrote:
After doing a great amount of reading and experimentation I decided
that the existing methods for creating read-only tables in Lua 5.1
was untenable.
For such a simple thing it seemed rather ridiculous to have to use an
empty proxy-table along with or instead of redefinitions of the
several table related functions.
It seemed that there should be a simpler solution.  So I went about
poking around in the code and came up with a solution that works for
me.  You can find the patch below that adds Read-Only table support
to Lua-5.1.1.
This facility is controlled by the '__readonly' entry in a table's
metatable.  If the value of '__readonly' is a function it is called
with semantics identical to '__newindex'  that is:

	mt = { __readonly = function(table, key, value) print("Attempted to
modify a read-only table!") end }

If the value of '__readonly' is anything other than a function a
runtime error is generated.  I'm sure if I were a little more
acquainted with the code that I could have been a little more clever
in this regard (such as taking a custom error as a string)

If I've done something horribly wrong, or have put this in an
inappropriate place, or there are other implications that I haven't
thought of please let me know.  This patch "feels" kind of hackish as
I am not intimately familiar with the code base, however, it seems to
be working find for me:

	mytab = { key1 = "val1", key2 = "val2" }
	mt = { __readonly = true }
	setmetatable(mytab, mt)
	mytab["key3"] = "val3"
	stdin:1: Attempt to modify a Read-Only Table!
	stack traceback:
        	stdin:1: in main chunk
	        [C]: ?

While some of the existing "solutions" are not too difficult to
accomplish in Lua, when trying to implement them in C, it becomes a
bit of a challenge for something that should be so simple, and in
fact the change to the code was very minimal.

Thank you for your time and consideration,

Michael Grubb

It is a rare thing to compile a piece of software without ANY
compiler warnings.  Thank you for your attention to detail.

The patch is below:

diff -ruN lua-rot-5.1.1/src/ltm.c lua-5.1.1/src/ltm.c
--- lua-rot-5.1.1/src/ltm.c	2006-11-10 15:39:56.000000000 -0600
+++ lua-5.1.1/src/ltm.c	2006-01-10 06:50:00.000000000 -0600
@@ -30,11 +30,8 @@
void luaT_init (lua_State *L) {
   static const char *const luaT_eventname[] = {  /* ORDER TM */
     "__index", "__newindex",
-    "__gc", "__mode",
-	"__readonly",
-	"__eq", "__add", "__sub", "__mul", "__div", "__mod",
+    "__gc", "__mode", "__eq",
+    "__add", "__sub", "__mul", "__div", "__mod",
     "__pow", "__unm", "__len", "__lt", "__le",
     "__concat", "__call"
diff -ruN lua-rot-5.1.1/src/ltm.h lua-5.1.1/src/ltm.h
--- lua-rot-5.1.1/src/ltm.h	2006-11-10 15:39:59.000000000 -0600
+++ lua-5.1.1/src/ltm.h	2005-06-06 08:30:25.000000000 -0500
@@ -20,9 +20,6 @@
   TM_EQ,  /* last tag method with `fast' access */
diff -ruN lua-rot-5.1.1/src/luaconf.h lua-5.1.1/src/luaconf.h
--- lua-rot-5.1.1/src/luaconf.h	2006-11-10 15:39:47.000000000 -0600
+++ lua-5.1.1/src/luaconf.h	2006-04-10 13:27:23.000000000 -0500
@@ -747,12 +747,6 @@
-@@ LUA_READONLY_TABLES controls whether to enable read-only table
-support by way of the __readonly metatable entry. CHANGE it (define it)
-to enable Read-Only support.
=================================================================== * /
diff -ruN lua-rot-5.1.1/src/lvm.c lua-5.1.1/src/lvm.c
--- lua-rot-5.1.1/src/lvm.c	2006-11-10 14:26:17.000000000 -0600
+++ lua-5.1.1/src/lvm.c	2006-06-05 10:58:59.000000000 -0500
@@ -140,22 +140,11 @@
     if (ttistable(t)) {  /* `t' is a table? */
       Table *h = hvalue(t);
       TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
-	  if ( (tm = fasttm(L, h->metatable, TM_READONLY)) != NULL )
-	  {
-		  if (ttisfunction(tm)) {
-			  callTM(L, tm, t, key, val);
-			  return;
-		  }
-		  else
-			  luaG_runerror(L, "Attempt to modify a Read-Only
-	  }
       if (!ttisnil(oldval) ||  /* result is no nil? */
           (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /*
or no TM? */
-        	setobj2t(L, oldval, val);
-        	luaC_barriert(L, h, val);
-        	return;
+        setobj2t(L, oldval, val);
+        luaC_barriert(L, h, val);
+        return;
       /* else will try the tag method */