lua-users home
lua-l archive

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


Hello!

I just rustled up the following to help my code suck less.

From within Lua code it replaces pairs() and ipairs() with
versions that catch writes to the table that is being iterated
over.  Stick this in your code and you'll get a clear error
instead of undefined behaviour when writing to a table while
it's being iterated over by pairs() or ipairs().

BIG WARNING: If you break out of the loop early then you'll
have to explicitly remove the iterated table's protection
with Punprotect(table).  :(   :(

There are a couple of things that it doesn't catch.
One is the *removal* of table entries (ie. assignment of nil
to them) while iterating.  Couldn't think of a way to do that.
The other is nested iteration over the same table (I haven't
tested it, but I think that you'll lose protection one or
two nested levels down, nothing too serious).

Hope it's useful... anyway.


   local old_pairs, old_ipairs = pairs, ipairs;
   local function block_write(tab,idx,val)
      error("Attempted to write to a table that was being iterated over.");
   end
   local my_plain_metatable = {__newindex = block_write};
   function Punprotect(table)
      local mt = getmetatable(table);
      if ((mt == my_plain_metatable) or not mt) then
	 setmetatable(table, nil);
      else
	 mt.__newindex = mt.OLD__newindex;
	 mt.OLD__newindex = nil;
      end
   end
   local function Pprotect(table)
      local mt = getmetatable(table);
      if (mt) then
	 mt.OLD__newindex = mt.__newindex;
	 mt.__newindex = block_write;
      else
	 setmetatable(table, my_plain_metatable);
      end
   end
   local function mynext(table, index)
      local k,v = next(table, index);
      if (k == nil) then
	 Punprotect(table);
      end
      return k,v;
   end
   local function myinext(table, index)
      local k,v;
      if (index ~= nil) then
	 k = index+1;
      else
	 k = 1;
      end
      v = table[k]
      if (v == nil) then
	 Punprotect(table);
	 return nil;
      end
      return k,v;
   end
   pairs = function(tab)
	      Pprotect(tab);
	      return mynext, tab;
	   end
   ipairs = function(tab)
	       Pprotect(tab);
	       return myinext, tab;
	    end

--
Adam D. Moss   . ,,^^   adam@gimp.org   http://www.foxbox.org/   co:3
"Responsible parents don't raise kids in West Virginia."