lua-users home
lua-l archive

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


Hi list,

I want to share some little work I have done recently, mainly for my special
personal needs. maybe someone else would find it useful as well.

short description:

I use as glue logics for some native compiled modules. several functions would
return values to the Lua side code (as userdata, of course), which would then pass
these values to other functions as parameters.

in the wrapper/binding function, to dispatch the call, I need to know which of
the functions produced the values: they were of different types in the original
C++ side code, but are all void pointers now.

I feel I shall redesign the APIs exposed to Lua side, so as to solve the problem.
but since the library is incrementally evoled to what it is like today, while
the Lua side API can't be easily changed in a short time, I now use full userdata
to distinguash the types using metatables, since all light userdata share a single
metatable.

I feel it too much overhead using the full userdata just to pass some opaque values,
and I need to set a per-instance metatable everytime I produced that value.
indeed, they are just some opaque values passed to Lua then back to C++. Lua code
don't need to operate the values: no index, no call, no compare.

so I imagined, how nice there be more then one type of light userdata, at least at
the C API level.

thus my first try (attached varbits.patch): (ab)use the variant bits of the type tag
to store this extra information. I found Lua 5.2 allocates 2 bits for this purpose,
so now I have up to 4 different (sub)types of light userdata. that's totally enough
for my current use case.

then I thought, maybe I could go one step further, thus the second patch. I added a
secondary tag (what I called type extention, but sadlly I couldn't think out a better
name for that. maybe subtype?) into the TValue struct, I even give each subtype of
lightuserdata a seperate metatable.

currently, the type extension (or subtype) information is only visible to the C API,
Lua side code are not aware of the type extension information.


best regards.
diff --git a/src/lapi.c b/src/lapi.c
index d011431..b1f6be5 100644
--- a/src/lapi.c
+++ b/src/lapi.c
@@ -258,6 +258,12 @@ LUA_API const char *lua_typename (lua_State *L, int t) {
 }
 
 
+LUA_API int lua_typevariant (lua_State *L, int idx) {
+  const TValue *o = index2addr(L, idx);
+  return (rttype(o) & VARBITS) >> 4;
+}
+
+
 LUA_API int lua_iscfunction (lua_State *L, int idx) {
   StkId o = index2addr(L, idx);
   return (ttislcf(o) || (ttisCclosure(o)));
@@ -582,9 +588,10 @@ LUA_API void lua_pushboolean (lua_State *L, int b) {
 }
 
 
-LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
+LUA_API void lua_pushlightuserdatavariant (lua_State *L, void *p, int variant) {
   lua_lock(L);
-  setpvalue(L->top, p);
+  api_check(L, variant >= 0 && variant <= 3, "variant out of range 0-3");
+  setpvaluevariant(L->top, p, variant);
   api_incr_top(L);
   lua_unlock(L);
 }
diff --git a/src/lauxlib.c b/src/lauxlib.c
index b00f8c7..6b998d3 100644
--- a/src/lauxlib.c
+++ b/src/lauxlib.c
@@ -269,6 +269,22 @@ LUALIB_API int luaL_execresult (lua_State *L, int stat) {
 ** =======================================================
 */
 
+LUALIB_API void *luaL_testlightuserdata (lua_State *L, int lud, int variant) {
+  return lua_type(L, lud) == LUA_TLIGHTUSERDATA
+             && lua_typevariant(L, lud) == variant
+         ? lua_touserdata(L, lud)
+         : NULL;
+}
+
+
+LUALIB_API void *luaL_checklightuserdata (lua_State *L, int lud, int variant) {
+  void *p = luaL_testlightuserdata(L, lud, variant);
+  if (p == NULL)
+    luaL_argerror(L, lud, "variant bits don't match for light userdata");
+  return p;
+}
+
+
 LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
   luaL_getmetatable(L, tname);  /* try to get metatable */
   if (!lua_isnil(L, -1))  /* name already in use? */
diff --git a/src/lauxlib.h b/src/lauxlib.h
index 0fb023b..87364f7 100644
--- a/src/lauxlib.h
+++ b/src/lauxlib.h
@@ -51,6 +51,9 @@ LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
 LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
 LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
 
+LUALIB_API void *(luaL_testlightuserdata) (lua_State *L, int lud, int variant);
+LUALIB_API void *(luaL_checklightuserdata) (lua_State *L, int lud, int variant);
+
 LUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname);
 LUALIB_API void  (luaL_setmetatable) (lua_State *L, const char *tname);
 LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
diff --git a/src/lobject.h b/src/lobject.h
index 3a630b9..e455064 100644
--- a/src/lobject.h
+++ b/src/lobject.h
@@ -133,7 +133,7 @@ typedef struct lua_TValue TValue;
 #define ttisnumber(o)		checktag((o), LUA_TNUMBER)
 #define ttisnil(o)		checktag((o), LUA_TNIL)
 #define ttisboolean(o)		checktag((o), LUA_TBOOLEAN)
-#define ttislightuserdata(o)	checktag((o), LUA_TLIGHTUSERDATA)
+#define ttislightuserdata(o)	checktype((o), LUA_TLIGHTUSERDATA)
 #define ttisstring(o)		checktype((o), LUA_TSTRING)
 #define ttisshrstring(o)	checktag((o), ctb(LUA_TSHRSTR))
 #define ttislngstring(o)	checktag((o), ctb(LUA_TLNGSTR))
@@ -195,6 +195,9 @@ typedef struct lua_TValue TValue;
 #define setpvalue(obj,x) \
   { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }
 
+#define setpvaluevariant(obj,x,v) \
+  { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA|((v)<<4)); }
+
 #define setbvalue(obj,x) \
   { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }
 
diff --git a/src/lua.h b/src/lua.h
index 149a2c3..c790ff8 100644
--- a/src/lua.h
+++ b/src/lua.h
@@ -164,6 +164,7 @@ LUA_API int             (lua_iscfunction) (lua_State *L, int idx);
 LUA_API int             (lua_isuserdata) (lua_State *L, int idx);
 LUA_API int             (lua_type) (lua_State *L, int idx);
 LUA_API const char     *(lua_typename) (lua_State *L, int tp);
+LUA_API int             (lua_typevariant) (lua_State *L, int idx);
 
 LUA_API lua_Number      (lua_tonumberx) (lua_State *L, int idx, int *isnum);
 LUA_API lua_Integer     (lua_tointegerx) (lua_State *L, int idx, int *isnum);
@@ -213,7 +214,10 @@ LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
 LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
 LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
 LUA_API void  (lua_pushboolean) (lua_State *L, int b);
-LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API void  (lua_pushlightuserdatavariant) (lua_State *L, void *p,
+                                                            int variant);
+#define lua_pushlightuserdata(L,p) lua_pushlightuserdatavariant(L, (p), 0)
+
 LUA_API int   (lua_pushthread) (lua_State *L);
 
 
diff --git a/src/lapi.c b/src/lapi.c
index d011431..3a45582 100644
--- a/src/lapi.c
+++ b/src/lapi.c
@@ -581,6 +581,31 @@ LUA_API void lua_pushboolean (lua_State *L, int b) {
   lua_unlock(L);
 }
 
+#if defined(LUA_TYPEEXTENSION)
+
+LUA_API int lua_allocatetypex (lua_State *L) {
+  global_State *g = G(L);
+  luaM_growvector(L, g->mtx, g->usedttx, g->sizemtx, struct Table *, MAX_INT,
+                     "type extensions");
+  return g->usedttx++;
+}
+
+
+LUA_API int lua_typex(lua_State *L, int idx) {
+  const TValue *o = index2addr(L, idx);
+  return ttypex(o);
+}
+
+
+LUA_API void lua_pushlightuserdatax (lua_State *L, void *p, int x) {
+  lua_lock(L);
+  api_check(L, x >= 0 && x < G(L)->usedttx, "invalid typex value");
+  setxpvalue(L->top, p, x);
+  api_incr_top(L);
+  lua_unlock(L);
+}
+
+#else
 
 LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
   lua_lock(L);
@@ -589,6 +614,7 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
   lua_unlock(L);
 }
 
+#endif
 
 LUA_API int lua_pushthread (lua_State *L) {
   lua_lock(L);
@@ -696,6 +722,11 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
     case LUA_TUSERDATA:
       mt = uvalue(obj)->metatable;
       break;
+#if defined(LUA_TYPEEXTENSION)
+    case LUA_TLIGHTUSERDATA:
+      mt = G(L)->mtx[ttypex(obj)];
+      break;
+#endif
     default:
       mt = G(L)->mt[ttypenv(obj)];
       break;
@@ -838,6 +869,12 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
       }
       break;
     }
+#if defined(LUA_TYPEEXTENSION)
+    case LUA_TLIGHTUSERDATA: {
+      G(L)->mtx[ttypex(obj)] = mt;
+      break;
+    }
+#endif
     default: {
       G(L)->mt[ttypenv(obj)] = mt;
       break;
diff --git a/src/lauxlib.c b/src/lauxlib.c
index b00f8c7..bee1a27 100644
--- a/src/lauxlib.c
+++ b/src/lauxlib.c
@@ -269,6 +269,24 @@ LUALIB_API int luaL_execresult (lua_State *L, int stat) {
 ** =======================================================
 */
 
+#if defined(LUA_TYPEEXTENSION)
+
+LUALIB_API void *luaL_testlightuserdatax (lua_State *L, int lud, int x) {
+  return lua_type(L, lud) == LUA_TLIGHTUSERDATA && lua_typex(L, lud) == x
+         ? lua_touserdata(L, lud)
+         : NULL;
+}
+
+
+LUALIB_API void *luaL_checklightuserdatax (lua_State *L, int lud, int x) {
+  void *p = luaL_testlightuserdatax(L, lud, x);
+  if (p == NULL)
+    luaL_argerror(L, lud, "not light userdata or typex mismatch");
+  return p;
+}
+
+#endif
+
 LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
   luaL_getmetatable(L, tname);  /* try to get metatable */
   if (!lua_isnil(L, -1))  /* name already in use? */
diff --git a/src/lauxlib.h b/src/lauxlib.h
index 0fb023b..c542672 100644
--- a/src/lauxlib.h
+++ b/src/lauxlib.h
@@ -51,6 +51,11 @@ LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
 LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
 LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
 
+#if defined(LUA_TYPEEXTENSION)
+LUALIB_API void *luaL_testlightuserdatax (lua_State *L, int lud, int x);
+LUALIB_API void *luaL_checklightuserdatax (lua_State *L, int lud, int x);
+#endif
+
 LUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname);
 LUALIB_API void  (luaL_setmetatable) (lua_State *L, const char *tname);
 LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
diff --git a/src/lgc.c b/src/lgc.c
index 52460dc..ba01749 100644
--- a/src/lgc.c
+++ b/src/lgc.c
@@ -302,6 +302,10 @@ static void markmt (global_State *g) {
   int i;
   for (i=0; i < LUA_NUMTAGS; i++)
     markobject(g, g->mt[i]);
+#if defined(LUA_TYPEEXTENSION)
+  for (i=0; i < g->usedttx; i++)
+    markobject(g, g->mtx[i]);
+#endif
 }
 
 
diff --git a/src/lobject.h b/src/lobject.h
index 3a630b9..14a3b86 100644
--- a/src/lobject.h
+++ b/src/lobject.h
@@ -377,6 +377,40 @@ typedef struct lua_TValue TValue;
 /* }====================================================== */
 
 
+/*
+** {======================================================
+** type extension
+** =======================================================
+*/
+#if defined(LUA_TYPEEXTENSION)
+
+#undef TValuefields
+#define TValuefields	Value value_; int tt_; int ttx_
+
+#undef NILCONSTANT
+#define NILCONSTANT	{NULL}, LUA_TNIL, 0
+
+/* type tag extension of a TValue */
+#define ttypex(o)       ((o)->ttx_)
+#define setttx_(o,x)    ((o)->ttx_=(x))
+
+#undef setpvalue
+#define setpvalue(obj,x) setxpvalue(obj, x, 0)
+
+#define setxpvalue(obj,x,ttx) \
+  { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); \
+    setttx_(io, ttx); }
+
+#undef setobj
+#define setobj(L,obj1,obj2) \
+	{ const TValue *io2=(obj2); TValue *io1=(obj1); \
+	  io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
+	  io1->ttx_ = io2->ttx_; \
+	  checkliveness(G(L),io1); }
+#endif
+/* }====================================================== */
+
+
 
 /*
 ** {======================================================
diff --git a/src/lstate.c b/src/lstate.c
index c7f2672..70f927b 100644
--- a/src/lstate.c
+++ b/src/lstate.c
@@ -271,6 +271,20 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   if (l == NULL) return NULL;
   L = &l->l.l;
   g = &l->g;
+#if defined(LUA_TYPEEXTENSION)
+#define INITSIZEXMT 4
+  struct Table **mtx = cast(struct Table **, (*f)(ud, NULL, 0,
+                            INITSIZEXMT*sizeof(struct Table*)));
+  if (mtx == NULL) {
+    (*f)(ud, l, sizeof(LG), 0);
+    return NULL;
+  }
+  /* the zero value is for default ttx */
+  g->usedttx = 1;
+  g->sizemtx = INITSIZEXMT;
+  for (i=0; i < INITSIZEXMT; i++) mtx[i] = NULL; g->mtx = mtx;
+#undef INITSIZEXMT
+#endif
   L->next = NULL;
   L->tt = LUA_TTHREAD;
   g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
diff --git a/src/lstate.h b/src/lstate.h
index daffd9a..0f15c1a 100644
--- a/src/lstate.h
+++ b/src/lstate.h
@@ -145,6 +145,11 @@ typedef struct global_State {
   TString *memerrmsg;  /* memory-error message */
   TString *tmname[TM_N];  /* array with tag-method names */
   struct Table *mt[LUA_NUMTAGS];  /* metatables for basic types */
+#if defined(LUA_TYPEEXTENSION)
+  int usedttx;
+  int sizemtx;
+  struct Table **mtx;
+#endif
 } global_State;
 
 
diff --git a/src/ltm.c b/src/ltm.c
index 69b4ed7..a4d396f 100644
--- a/src/ltm.c
+++ b/src/ltm.c
@@ -69,6 +69,11 @@ const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
     case LUA_TUSERDATA:
       mt = uvalue(o)->metatable;
       break;
+#if defined(LUA_TYPEEXTENSION)
+    case LUA_TLIGHTUSERDATA:
+      mt = G(L)->mtx[ttypex(o)];
+      break;
+#endif
     default:
       mt = G(L)->mt[ttypenv(o)];
   }
diff --git a/src/lua.h b/src/lua.h
index 149a2c3..e747e30 100644
--- a/src/lua.h
+++ b/src/lua.h
@@ -213,10 +213,16 @@ LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
 LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
 LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
 LUA_API void  (lua_pushboolean) (lua_State *L, int b);
+#if defined(LUA_TYPEEXTENSION)
+LUA_API int   (lua_allocatetypex) (lua_State *L);
+LUA_API int   (lua_typex) (lua_State *L, int idx);
+LUA_API void  (lua_pushlightuserdatax) (lua_State *L, void *p, int x);
+#define lua_pushlightuserdata(L,p) lua_pushlightuserdatax(L, p, 0)
+#else
 LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
+#endif
 LUA_API int   (lua_pushthread) (lua_State *L);
 
-
 /*
 ** get functions (Lua -> stack)
 */
diff --git a/src/luaconf.h b/src/luaconf.h
index 18be9a9..5b78eff 100644
--- a/src/luaconf.h
+++ b/src/luaconf.h
@@ -545,6 +545,15 @@
 ** without modifying the main part of the file.
 */
 
+#if !defined(LUA_TYPEEXTENSION)
+#if !defined(LUA_NANTRICK)
+#define LUA_TYPEEXTENSION
+#endif
+#else
+#undef LUA_NANTRICK
+#endif
+
 
 
 #endif
diff --git a/src/lvm.c b/src/lvm.c
index 141b9fd..9ce4d3e 100644
--- a/src/lvm.c
+++ b/src/lvm.c
@@ -264,7 +264,11 @@ int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) {
     case LUA_TNIL: return 1;
     case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
     case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2);  /* true must be 1 !! */
-    case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
+    case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2)
+#if defined(LUA_TYPEEXTENSION)
+                                    && ttypex(t1) == ttypex(t2)
+#endif
+                                    ;
     case LUA_TLCF: return fvalue(t1) == fvalue(t2);
     case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
     case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));