[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Enabling __eq metamethod for numbers
- From: Nathan Page <nathanrpage97@...>
- Date: Thu, 20 Jan 2022 22:16:54 -0800
Hi All,
I have been embedding lua 5.4.x into an existing application. I use
the bint library to help with larger integers, but came up on this
piece of information from the reference manual:
> Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean.
This is an issue for my use case where I will be frequently working
with integers > 64 bits in size so the bint library is needed. To
remedy this, I modified the lvm.c file as such to enable the __eq
metamethod for user_data or tables compared to a number. (see end of
email for the diff)
It seems to have been working so far. The only modification I had to
make was adding a type check to any library's __eq metamethod that
didn't expect to receive a number to prevent errors.
Is there anything I'm missing with this modification that would come
back to bite me? For my use case, the performance drop doesn't matter.
Thanks,
Nathan
bint: https://github.com/edubart/lua-bint
lvm.c changes (line numbers will differ due to clang-format):
@@ -521,9 +521,21 @@
int luaV_equalobj(lua_State *L, const TValue *t1, const TValue *t2) {
const TValue *tm;
if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */
- if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
- return 0; /* only numbers can be equal with different variants */
- else { /* two numbers with different variants */
+ if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) {
+ if (ttype(t1) == LUA_TNUMBER || ttype(t2) != LUA_TNUMBER) {
+ return 0;
+ }
+ switch (ttypetag(t1)) {
+ case LUA_VUSERDATA:
+ tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
+ goto run_tm;
+ case LUA_VTABLE:
+ tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
+ goto run_tm;
+ default:
+ return 0;
+ }
+ } else { /* two numbers with different variants */
/* One of them is an integer. If the other does not have an
integer value, they cannot be equal; otherwise, compare their
integer values. */
@@ -570,6 +582,7 @@
default:
return gcvalue(t1) == gcvalue(t2);
}
+run_tm:
if (tm == NULL) /* no TM? */
return 0; /* objects are different */
else {
@@ -1534,9 +1547,9 @@
vmbreak;
}
vmcase(OP_EQK) {
+ int cond;
TValue *rb = KB(i);
- /* basic types do not use '__eq'; we can use raw equality */
- int cond = luaV_rawequalobj(s2v(ra), rb);
+ Protect(cond = luaV_equalobj(L, s2v(ra), rb));
docondjump();
vmbreak;
}
@@ -1547,8 +1560,11 @@
cond = (ivalue(s2v(ra)) == im);
else if (ttisfloat(s2v(ra)))
cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im));
- else
- cond = 0; /* other types cannot be equal to a number */
+ else {
+ TValue im_tv;
+ setivalue(&im_tv, im);
+ Protect(cond = luaV_equalobj(L, s2v(ra), &im_tv));
+ }
docondjump();
vmbreak;
}