Custom type methods (and name)

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

Custom type methods (and name)

Ivan-2
Hello!

I’m trying to create my own type (for complex numbers) and use it within Lua scripts. It’s okay but the problem is that in some messages this type is denoted as ‘userdata’ which looks ugly. I have found that if type’s metatable has __name field, that it’s value is used as a type name. Unfortunately this key is not used everywhere, e.g. pairs(complex) still uses ‘userdata’ name. Is there a way I can make Lua to use custom type name?

And a second question dedicated to the custom types. Say I have two arrays of luaL_Reg’s: _methods (instance methods) and _meta (metatable with magic methods). _methods array contains methods that are to be called for a particular instance and are used to manipulate it. _meta array contains metamethods (__add, __sub and so on). The problem is related to [] operator, used to index arrays. I use the following code to configure this:

luaL_newmetatable(LUA, "array");
lua_pushstring(LUA, "__name");
lua_pushstring(LUA, "array");
lua_settable(LUA, -3);
luaL_openlib(LUA, nullptr, _meta, 0);
luaL_openlib(LUA, "array", _methods, 0);

lua_pushliteral(LUA, "__index"); 
lua_pushvalue(LUA, -3);  
lua_rawset(LUA, -3); 

lua_pop(LUA, 1);
If use the given code, my __index handler is called, but it is called every time I’m trying to call an instance method of class, for example for:

a:foo()

__index handler is called instead of foo(). If I change the code to (entirely taken from here: http://www.lua.org/pil/28.4.html):

luaL_newmetatable(LUA, SEQ_CLASS"_MT");
luaL_openlib(LUA, SEQ_CLASS, _meta, 0);
lua_pushstring(LUA, "__index");
lua_pushstring(LUA, "get");
lua_gettable(LUA, 2);  /* get seq.get */
lua_settable(LUA, 1);  /* metatable.__index = seq.get */

lua_pushstring(LUA, "__newindex");
lua_pushstring(LUA, "set");
lua_gettable(LUA, 2); /* get array.set */
lua_settable(LUA, 1); /* metatable.__newindex = seq.set */

lua_settop(LUA, top); // clean up

methods are successfully called, but I’m not able to use the [] operator because it returns nil for every key. 

I know that the PIL book contains example of a custom array class, but it shows how to use either OOP-like access (with get() and set() methods) or array-like access (with operators) and I need to combine there two approaches: to have methods and be able to use operators.

I’m using LuaJIT which is almost equal to Lua 5.1.

——— 
Best regards
Ivan
Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Sean Conner
It was thus said that the Great Ivan once stated:
> Hello!
>
> I’m trying to create my own type (for complex numbers) and use it within
> Lua scripts. It’s okay but the problem is that in some messages this type
> is denoted as ‘userdata’ which looks ugly. I have found that if type’s
> metatable has __name field, that it’s value is used as a type name.
> Unfortunately this key is not used everywhere, e.g. pairs(complex) still
> uses ‘userdata’ name. Is there a way I can make Lua to use custom type
> name?

  Try setting the __tostring() metamethod to print out how you want it to
look.  

> And a second question dedicated to the custom types. Say I have two arrays
> of luaL_Reg’s: _methods (instance methods) and _meta (metatable with magic
> methods). _methods array contains methods that are to be called for a
> particular instance and are used to manipulate it. _meta array contains
> metamethods (__add, __sub and so on).

  It may not be clear in the manual, but use the following quite often
(example from
https://github.com/spc476/lua-conmanorg/blob/faf0780d01700a45e2af5d52f2c732fa45092c1e/src/fsys.c#L1180):

static const luaL_Reg m_dir_meta[] =
{
  { "__tostring"        , dir_meta___tostring   } ,
  { "__gc"              , dir_meta___gc         } ,
  { "rewind"            , dir_meta_rewind       } ,
  { "next"              , dir_meta_next         } ,
  { NULL                , NULL                  }
};

  luaL_newmetatable(L,TYPE_DIR);
#if LUA_VERSION_NUM == 501
  luaL_register(L,NULL,m_dir_meta);
#else
  luaL_setfuncs(L,m_dir_meta,0);
#endif

  lua_pushvalue(L,-1);
  lua_setfield(L,-2,"__index");

The luaL_Reg[] contains both methods and metamethods, and the __index field
of the metatable is set to itself so the regular methods can be found.  So:

> fsys = require "org.conman.fsys"

        loads the module

> dir = fsys.opendir(".")

        returns an instance of this type

> print(dir)
directory (0x7fb2c1501ac8)

        calls the __tostring() metamethod

> print(dir:next())
base64.c
> print(dir:next())
clock.c

        calls a method (next()) which is stored in the metatable.

> print(dir)
directory (0x7fb2c1501ac8)

        and just to show, another indirect call to __tostring()

  -spc (Does this solve your problem?)



Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Dirk Laurie-2
In reply to this post by Ivan-2
2016-02-18 22:46 GMT+02:00 Ivan <[hidden email]>:
> I’m trying to create my own type (for complex numbers) and
> use it within Lua scripts.

This has been done by Luiz himself (Google: complex lua lhf).

> It’s okay but the problem is that in some messages this type is
> denoted as ‘userdata’ which looks ugly. I have found that if type’s
> metatable has __name field, that it’s value is used as a type name.
> Unfortunately this key is not used everywhere, e.g. pairs(complex)
> still uses ‘userdata’ name. Is there a way I can make Lua to use
> custom type name?

Under Lua 5.3.2 with Luiz's complex, I get:

> z=complex.new(0,1); pairs(z)
stdin:1: bad argument #1 to 'pairs' (table expected, got complex number)

So I can't reproduce your result.

The manual says only:

    The entry __name is used by some error-reporting functions.

The word "some" usually means "at least one but not all". I suspect
that the vagueness is deliberate, and if you could provide a list of
which error messages you would like to have included too, some (!)
might get into Lua 5.3.3.

Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Ivan-2


19.02.2016, 08:14, "Dirk Laurie" <[hidden email]>:
> 2016-02-18 22:46 GMT+02:00 Ivan <[hidden email]>:
>>  I’m trying to create my own type (for complex numbers) and
>>  use it within Lua scripts.
>
> This has been done by Luiz himself (Google: complex lua lhf).

Ok, but complex is not the only custom type I need.

>
>>  It’s okay but the problem is that in some messages this type is
>>  denoted as ‘userdata’ which looks ugly. I have found that if type’s
>>  metatable has __name field, that it’s value is used as a type name.
>>  Unfortunately this key is not used everywhere, e.g. pairs(complex)
>>  still uses ‘userdata’ name. Is there a way I can make Lua to use
>>  custom type name?
>
> Under Lua 5.3.2 with Luiz's complex, I get:
>
>>  z=complex.new(0,1); pairs(z)
>
> stdin:1: bad argument #1 to 'pairs' (table expected, got complex number)
>
> So I can't reproduce your result.
>
> The manual says only:
>
>     The entry __name is used by some error-reporting functions.
>
> The word "some" usually means "at least one but not all". I suspect
> that the vagueness is deliberate, and if you could provide a list of
> which error messages you would like to have included too, some (!)
> might get into Lua 5.3.3.

My code:

local cx = complex.new(0, 0)

local cx2 = complex.new(1, 1)

print(cx < cx2)

and the mesasge 'attempt to compare two userdata values'

It seems that I have to implement all of the metamethods (even those that are not supported by the domain) nad provide my own error messages.

-- --

And still I'm looking for a way to use both OOP style (my_array:length()) and array-style (my_array[1] = 0)


Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Luiz Henrique de Figueiredo
In reply to this post by Dirk Laurie-2
> Under Lua 5.3.2 with Luiz's complex, I get:
>
> > z=complex.new(0,1); pairs(z)
> stdin:1: bad argument #1 to 'pairs' (table expected, got complex number)
>
> So I can't reproduce your result.
>
> The manual says only:
>
>     The entry __name is used by some error-reporting functions.

__name support was introduced in Lua 5.3: luaL_newmetatable sets it,
which automatically gives all libraries nicer error messages.

The OP is using Lua 5.1.

Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Luiz Henrique de Figueiredo
In reply to this post by Ivan-2
> local cx = complex.new(0, 0)
> local cx2 = complex.new(1, 1)
> print(cx < cx2)
>
> and the mesasge 'attempt to compare two userdata values'

Indeed, my complex library does not set the __lt metamethod since I thought
it made no sense.

If you want better error message, do

complex.__lt=function () error"complex numbers cannot be compared" end


Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Ivan-2
In reply to this post by Luiz Henrique de Figueiredo


19.02.2016, 13:04, "Luiz Henrique de Figueiredo" <[hidden email]>:

>>  Under Lua 5.3.2 with Luiz's complex, I get:
>>
>>  > z=complex.new(0,1); pairs(z)
>>  stdin:1: bad argument #1 to 'pairs' (table expected, got complex number)
>>
>>  So I can't reproduce your result.
>>
>>  The manual says only:
>>
>>      The entry __name is used by some error-reporting functions.
>
> __name support was introduced in Lua 5.3: luaL_newmetatable sets it,
> which automatically gives all libraries nicer error messages.
>
> The OP is using Lua 5.1.

My last message about problem with (<) operator is realted to 5.3. Here is part 5.3's code:

l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
  const char *t1 = objtypename(p1);
  const char *t2 = objtypename(p2);

  if (t1 == t2)
    luaG_runerror(L, "attempt to compare two %s values", t1);
  else
    luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
}

So, objtypename() is used to get name of type:

#define ttypename(x) luaT_typenames_[(x) + 1]
#define objtypename(x) ttypename(ttnov(x))

As one can see, __name is not used at all for this messages.

Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Ivan-2
In reply to this post by Luiz Henrique de Figueiredo


19.02.2016, 13:08, "Luiz Henrique de Figueiredo" <[hidden email]>:

>>  local cx = complex.new(0, 0)
>>  local cx2 = complex.new(1, 1)
>>  print(cx < cx2)
>>
>>  and the mesasge 'attempt to compare two userdata values'
>
> Indeed, my complex library does not set the __lt metamethod since I thought
> it made no sense.
>
> If you want better error message, do
>
> complex.__lt=function () error"complex numbers cannot be compared" end

You right, comlexes cannot be ordered, but a particular user may be confused by a strange message about mysterious 'userdata' while they work with complex numbers.

Reply | Threaded
Open this post in threaded view
|

Re: Custom type methods (and name)

Ericson Carlos
In reply to this post by Ivan-2
On Fri, Feb 19, 2016 at 5:01 PM, Ivan <[hidden email]> wrote:


And still I'm looking for a way to use both OOP style (my_array:length()) and array-style (my_array[1] = 0)



​Put an if-else in the "get" function such that if the argument is a number you do the array access, else you search the metatable. Something like:

static int get (lua_State *L) {
    array* a = check_array (L);
    if (lua_isnumber (L, 2)) {
      int index = lua_tointeger (L, 2);
      <... array access here ...>
      return 1;
    }
    else {
      lua_getglobal (L, "array");
      lua_pushvalue (L, 2);
      lua_gettable (L, -2);
      lua_remove (L, -2);
      return 1;
    }
  }