default LUA_TTABLE metatable, like for strings

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

default LUA_TTABLE metatable, like for strings

Pavel
Proposal:
While table metatable is not set, make it "t->metatable =
G(L)->mt[LUA_TTABLE]" by default, instead of "t->metatable =
NULL".
Access from Lua could be provided by special string e.g.
getmetatable("table")
And creation of this default metatable in tablelib init, like
for strings, additionally __index could also be set to
tablelib.


@ltable.c, line 421:
Table *luaH_new (lua_State *L) {
   GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table));
   Table *t = gco2t(o);
-  t->metatable = NULL;
+  t->metatable = G(L)->mt[LUA_TTABLE];
   t->flags = cast_byte(~0);
   t->array = NULL;
   t->sizearray = 0;
   setnodevector(L, t, 0);
   return t;
}

@lapi.c,, line 697:
LUA_API int lua_getmetatable (lua_State *L, int objindex) {
   const TValue *obj;
   Table *mt;
   int res = 0;
   lua_lock(L);
   obj = index2addr(L, objindex);
   switch (ttnov(obj)) {
     case LUA_TTABLE:
       mt = hvalue(obj)->metatable;
       break;
     case LUA_TUSERDATA:
       mt = uvalue(obj)->metatable;
       break;
+    case LUA_TSTRING: {
+      if (0 == strcmp(lua_tostring(L, objindex), "table"))
+        mt = G(L)->mt[LUA_TTABLE];
+      else
+        mt = G(L)->mt[LUA_TSTRING];
+      break;
+    }
     default:
       mt = G(L)->mt[ttnov(obj)];
       break;
   }
   if (mt != NULL) {
     sethvalue(L, L->top, mt);
     api_incr_top(L);
     res = 1;
   }
   lua_unlock(L);
   return res;
}

@lapi.c, line 846:
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
   TValue *obj;
   Table *mt;
   lua_lock(L);
   api_checknelems(L, 1);
   obj = index2addr(L, objindex);
   if (ttisnil(L->top - 1))
     mt = NULL;
   else {
     api_check(L, ttistable(L->top - 1), "table expected");
     mt = hvalue(L->top - 1);
   }
   switch (ttnov(obj)) {
     case LUA_TTABLE: {
       hvalue(obj)->metatable = mt;
       if (mt) {
         luaC_objbarrier(L, gcvalue(obj), mt);
         luaC_checkfinalizer(L, gcvalue(obj), mt);
       }
       break;
     }
     case LUA_TUSERDATA: {
       uvalue(obj)->metatable = mt;
       if (mt) {
         luaC_objbarrier(L, uvalue(obj), mt);
         luaC_checkfinalizer(L, gcvalue(obj), mt);
       }
       break;
     }
+    case LUA_TSTRING: {
+      if (0 == strcmp(lua_tostring(L, objindex), "table"))
+        G(L)->mt[LUA_TTABLE] = mt;
+      else
+        G(L)->mt[LUA_TSTRING] = mt;
+      break;
+    }
     default: {
       G(L)->mt[ttnov(obj)] = mt;
       break;
     }
   }
   L->top--;
   lua_unlock(L);
   return 1;
}

@ltablib.c, line 440:
+static void createmetatable (lua_State *L) {
+  lua_createtable(L, 0, 1);  /* table to be metatable for
tables */
+  lua_pushliteral(L, "table");  /* 'table' string */
+  lua_pushvalue(L, -2);  /* copy table */
+  lua_setmetatable(L, -2);  /* set table as metatable for
tables */
+  lua_pop(L, 1);  /* pop 'table' string */
+  lua_pushvalue(L, -2);  /* get table library */
+  lua_setfield(L, -2, "__index");  /* metatable.__index =
table */
+  lua_pop(L, 1);  /* pop metatable */
+}

LUAMOD_API int luaopen_table (lua_State *L) {
   luaL_newlib(L, tab_funcs);
+  createmetatable(L);
#if defined(LUA_COMPAT_UNPACK)
   /* _G.unpack = table.unpack */
   lua_getfield(L, -1, "unpack");
   lua_setglobal(L, "unpack");
#endif
   return 1;
}

regards,
Pavel

Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Luiz Henrique de Figueiredo
What problem does this proposal solve?

Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Pavel
On Sun, 17 Mar 2019 12:42:32 -0300
  Luiz Henrique de Figueiredo <[hidden email]> wrote:
> What problem does this proposal solve?

Absence of default metatables.

This results, in some cases, that all the tables needs to be
created by some additional constructor function, that set the
metatable, which is a bit annoying, instead of setting it
once, for all the tables.

Which actually is not a problem, but more like a syntactic
sugar.

Plus it makes it uniform with the strings & strings library.
("string"):reverse()
({1,5,2,4,3}):sort()

Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Dirk Laurie-2
Op So. 17 Mrt. 2019 om 18:25 het temp 213 qwe <[hidden email]> geskryf:

>
> On Sun, 17 Mar 2019 12:42:32 -0300
>   Luiz Henrique de Figueiredo <[hidden email]> wrote:
> > What problem does this proposal solve?
>
> Absence of default metatables.
>
> This results, in some cases, that all the tables needs to be
> created by some additional constructor function, that set the
> metatable, which is a bit annoying, instead of setting it
> once, for all the tables.
>
> Which actually is not a problem, but more like a syntactic
> sugar.
>
> Plus it makes it uniform with the strings & strings library.
> ("string"):reverse()
> ({1,5,2,4,3}):sort()

Since 'table' is Lua's only data structure, which does duty for
arrays, sets, dictionaries, lists, queues, stacks, XML encodings,
matrices etc, it would be quite annoying to find every table one
creates pre-equipped with a metatable that serves only one of these.

Here is my full and complete class system. It is written to be the
body of a file but I often simply insert the code sans comments and
final 'return' at the top of a program.

--[[    Minimal support for classes
  Returns the class definer "class", and creates a global function "is"
    unless the global variable "is" is already in use.

USAGE
  1. Define a new class:   MyClass = class("my class",index,newindex)
       'index(class,key)' and 'newindex(object,key,value)' are optional
       metamethods. 'index' can be used to inherit an existing class.
  2. Add methods to it:    MyClass.method = function(self,...)"
                    or:    function MyClass:method(...)
  3. Reserved method:      MyClass:init(...)  [optional]
  4. Create new object     obj = MyClass(...)"
       If MyClass.init has been defined, obj:init(...) will be called before
       returning the object.

HOW IT WORKS
  MyClass is the metatable, the method table and (via its __call metamethod)
the default constructor for the new class, all rolled into one.
--]]

local new = function(class,...)
  local object = setmetatable({},class)
  if object.init then object:init(...) end
  return object
end

local class = function(name,index,newindex)
  local mt = setmetatable({__name=name,__newindex=newindex},
    {__call=new,__index=index})
  mt.__index = mt
  return mt
end

local is = function(class,object)
  return getmetatable(object) == class
end

if _ENV.is==nil then _ENV.is = is end
return class

Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Philippe Verdy
In reply to this post by Pavel
Why was the simple syntax addition for table constructors not augmented to use a ":" operator between table constructors, where "table1:table2" means attaching table2 as a metatable of table1? (I suggested using ":" for that purpose, but you may design another notation if you think it conflicts with other syntax, e.g. using "~" or ":=" or simply "=").
We should not even need to call setmetatable() with simple constructors.
Then what we do in LuaC is another problem. But probably a Lua state may also have an API to set a default metatable (defined as some "global" in the Lua state context), even if we can override this context by using "table1:null" with the same notation in Lua to explicitly remove this default.

Le dim. 17 mars 2019 à 16:03, temp 213 qwe <[hidden email]> a écrit :
Proposal:
While table metatable is not set, make it "t->metatable =
G(L)->mt[LUA_TTABLE]" by default, instead of "t->metatable =
NULL".
Access from Lua could be provided by special string e.g.
getmetatable("table")
And creation of this default metatable in tablelib init, like
for strings, additionally __index could also be set to
tablelib.


@ltable.c, line 421:
Table *luaH_new (lua_State *L) {
   GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table));
   Table *t = gco2t(o);
-  t->metatable = NULL;
+  t->metatable = G(L)->mt[LUA_TTABLE];
   t->flags = cast_byte(~0);
   t->array = NULL;
   t->sizearray = 0;
   setnodevector(L, t, 0);
   return t;
}

@lapi.c,, line 697:
LUA_API int lua_getmetatable (lua_State *L, int objindex) {
   const TValue *obj;
   Table *mt;
   int res = 0;
   lua_lock(L);
   obj = index2addr(L, objindex);
   switch (ttnov(obj)) {
     case LUA_TTABLE:
       mt = hvalue(obj)->metatable;
       break;
     case LUA_TUSERDATA:
       mt = uvalue(obj)->metatable;
       break;
+    case LUA_TSTRING: {
+      if (0 == strcmp(lua_tostring(L, objindex), "table"))
+        mt = G(L)->mt[LUA_TTABLE];
+      else
+        mt = G(L)->mt[LUA_TSTRING];
+      break;
+    }
     default:
       mt = G(L)->mt[ttnov(obj)];
       break;
   }
   if (mt != NULL) {
     sethvalue(L, L->top, mt);
     api_incr_top(L);
     res = 1;
   }
   lua_unlock(L);
   return res;
}

@lapi.c, line 846:
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
   TValue *obj;
   Table *mt;
   lua_lock(L);
   api_checknelems(L, 1);
   obj = index2addr(L, objindex);
   if (ttisnil(L->top - 1))
     mt = NULL;
   else {
     api_check(L, ttistable(L->top - 1), "table expected");
     mt = hvalue(L->top - 1);
   }
   switch (ttnov(obj)) {
     case LUA_TTABLE: {
       hvalue(obj)->metatable = mt;
       if (mt) {
         luaC_objbarrier(L, gcvalue(obj), mt);
         luaC_checkfinalizer(L, gcvalue(obj), mt);
       }
       break;
     }
     case LUA_TUSERDATA: {
       uvalue(obj)->metatable = mt;
       if (mt) {
         luaC_objbarrier(L, uvalue(obj), mt);
         luaC_checkfinalizer(L, gcvalue(obj), mt);
       }
       break;
     }
+    case LUA_TSTRING: {
+      if (0 == strcmp(lua_tostring(L, objindex), "table"))
+        G(L)->mt[LUA_TTABLE] = mt;
+      else
+        G(L)->mt[LUA_TSTRING] = mt;
+      break;
+    }
     default: {
       G(L)->mt[ttnov(obj)] = mt;
       break;
     }
   }
   L->top--;
   lua_unlock(L);
   return 1;
}

@ltablib.c, line 440:
+static void createmetatable (lua_State *L) {
+  lua_createtable(L, 0, 1);  /* table to be metatable for
tables */
+  lua_pushliteral(L, "table");  /* 'table' string */
+  lua_pushvalue(L, -2);  /* copy table */
+  lua_setmetatable(L, -2);  /* set table as metatable for
tables */
+  lua_pop(L, 1);  /* pop 'table' string */
+  lua_pushvalue(L, -2);  /* get table library */
+  lua_setfield(L, -2, "__index");  /* metatable.__index =
table */
+  lua_pop(L, 1);  /* pop metatable */
+}

LUAMOD_API int luaopen_table (lua_State *L) {
   luaL_newlib(L, tab_funcs);
+  createmetatable(L);
#if defined(LUA_COMPAT_UNPACK)
   /* _G.unpack = table.unpack */
   lua_getfield(L, -1, "unpack");
   lua_setglobal(L, "unpack");
#endif
   return 1;
}

regards,
Pavel

Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Sean Conner
In reply to this post by Dirk Laurie-2
It was thus said that the Great Dirk Laurie once stated:

>
> Here is my full and complete class system. It is written to be the
> body of a file but I often simply insert the code sans comments and
> final 'return' at the top of a program.
>
> local new = function(class,...)
>   local object = setmetatable({},class)
>   if object.init then object:init(...) end
>   return object
> end
>
> local class = function(name,index,newindex)
>   local mt = setmetatable({__name=name,__newindex=newindex},
>     {__call=new,__index=index})
>   mt.__index = mt
>   return mt
> end
>
> local is = function(class,object)
>   return getmetatable(object) == class
> end
>
> if _ENV.is==nil then _ENV.is = is end
> return class

  Why not make this code its own module?  It seems useful enough.

  -spc


Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Pavel
In reply to this post by Dirk Laurie-2
On Sun, 17 Mar 2019 22:40:34 +0200
  Dirk Laurie <[hidden email]> wrote:

> Op So. 17 Mrt. 2019 om 18:25 het temp 213 qwe
><[hidden email]> geskryf:
>>
>> On Sun, 17 Mar 2019 12:42:32 -0300
>>   Luiz Henrique de Figueiredo <[hidden email]>
>>wrote:
>> > What problem does this proposal solve?
>>
>> Absence of default metatables.
>>
> Since 'table' is Lua's only data structure, which does duty for
> arrays, sets, dictionaries, lists, queues, stacks, XML encodings,
> matrices etc, it would be quite annoying to find every table one
> creates pre-equipped with a metatable that serves only one of these.

It affects only the tables WITHOUT a metatable, as soon as you
setmetatable(), table would use it instead.
If arrays, sets, dictionaries, lists, queues, stacks, XML
encodings, matrices, etc do not set/change own metatable, it
means that it do not use any metamethods either, except
__index/__newindex.
But yes, theoreticaly you could break the normal operation of
other things by redefining __index, __newindex metamethods by
default to something strange.
And there is no need to set G(L)->mt[LUA_TTABLE] to {__index =
table}, like it is done for the strings with {__index = string
library}, G(L)->mt[LUA_TTABLE] could also be NULL by default,
but see no disadvantage here, why not to make it for tables,
if it was done like this for strings.

Reply | Threaded
Open this post in threaded view
|

Re: default LUA_TTABLE metatable, like for strings

Soni "They/Them" L.


On 2019-03-17 7:14 p.m., temp 213 qwe wrote:

> On Sun, 17 Mar 2019 22:40:34 +0200
>  Dirk Laurie <[hidden email]> wrote:
>> Op So. 17 Mrt. 2019 om 18:25 het temp 213 qwe <[hidden email]>
>> geskryf:
>>>
>>> On Sun, 17 Mar 2019 12:42:32 -0300
>>>   Luiz Henrique de Figueiredo <[hidden email]> wrote:
>>> > What problem does this proposal solve?
>>>
>>> Absence of default metatables.
>>>
>> Since 'table' is Lua's only data structure, which does duty for
>> arrays, sets, dictionaries, lists, queues, stacks, XML encodings,
>> matrices etc, it would be quite annoying to find every table one
>> creates pre-equipped with a metatable that serves only one of these.
>
> It affects only the tables WITHOUT a metatable, as soon as you
> setmetatable(), table would use it instead.
> If arrays, sets, dictionaries, lists, queues, stacks, XML encodings,
> matrices, etc do not set/change own metatable, it means that it do not
> use any metamethods either, except __index/__newindex.
> But yes, theoreticaly you could break the normal operation of other
> things by redefining __index, __newindex metamethods by default to
> something strange.
> And there is no need to set G(L)->mt[LUA_TTABLE] to {__index = table},
> like it is done for the strings with {__index = string library},
> G(L)->mt[LUA_TTABLE] could also be NULL by default, but see no
> disadvantage here, why not to make it for tables, if it was done like
> this for strings.
>

tablemt = {
__index = function(t, k)
return setmetatable({}, {__index=tablemt.__index, name=k,
other_debug_info=here})
end
}

this would be pretty neat tbh