xywy1985 发表于 2023-6-27 14:52

Tolua#下C及C#层的lua_typename方式的差异

问题

上文
分袂在C和C#层实现了Dump Lua Stack,
对Stack中的table类型,还进行了展开。
如果是table中的value是userdata、lightuserdata等类型,就会把lua_typename打印出来。

使用时发现,对lightuserdata类型,C层打印出来的是userdata,C#层打印出来的倒是lightuserdata,如下图(注意,我在这里分袂加了”-lua-“和”-C#-“):


C、C#版的逻辑是一样的,而C#版的方式理论上城市调到C层的native方式,理论上应该有不异的打印。
于是,研究一下为什么会分歧。
分析

C#层实现了本身的lua_typename

直接上源码:
/////---------Assets\ToLua\Examples\001_fp64\TestFP64Helper.cs
                        else
            {
                string typeName = L.LuaTypeName(L.LuaType(-1));
                result += typeName ?? ”nil”;
                result += ”\n”;
            }
/////---------Assets\ToLua\Core\LuaStatePtr.cs
      public LuaTypes LuaType(int index)
      {
            return LuaDLL.lua_type(L, index);
      }

      public string LuaTypeName(LuaTypes type)
      {
            return LuaDLL.lua_typename(L, type);
      }
/////---------Assets\ToLua\Core\LuaDLL.cs
      
      public static extern LuaTypes lua_type(IntPtr luaState, int index);


      public static string[] LuaTypeName = { ”none”, ”nil”, ”boolean”, ”lightuserdata”, ”number”, ”string”, ”table”, ”function”, ”userdata”, ”thread” };      

      public static string lua_typename(IntPtr luaState, LuaTypes type)
      {
            int t = (int)type;
            return LuaTypeName;
      }
按照代码我们知道,Tolua#的lua_type等方式,是会直接调用到native层的实现的,C#层只是一层包装。
而lua_typename纷歧样,lua_typename方式并没有直接调用native的lua_typename,
而是直接在C#层实现了一遍,直接用了C#层的方式,
LuaTypeName中定义了”lightuserdata”。
C层的实现

对应地,看一下在C层的,和C#的LuaTypeName“地位对等”的数组及相关代码。
代码如下:
/////---------tolua_runtime\luafixedpoint64\fp64helper.c
        else
    {
      const char *typeName = lua_typename(L, lua_type(L, -1));
      result = _malloc_concatenate_spaced_strings_free_first(result, typeName ? typeName : ”nil”);
      result = _malloc_concatenate_strings_free_first(result, ”\n”);
    }

/////---------tolua_runtime\macnojit\lua\lapi.c
/////---------tolua_runtime\luajit-2.1\src\host\minilua.c
LUA_API const char *lua_typename (lua_State *L, int t) {
UNUSED(L);
return (t == LUA_TNONE) ? ”no value” : luaT_typenames;
}

static const char*lua_typename(lua_State*L,int t){
UNUSED(L);
return(t==(-1))?”no value”:luaT_typenames;
}

/////---------tolua_runtime\macnojit\lua\ltm.c
/////---------tolua_runtime\luajit-2.1\src\host\minilua.c

static const char*const luaT_typenames[]={
”nil”,”boolean”,”userdata”,”number”,
”string”,”table”,”function”,”userdata”,”thread”,
”proto”,”upval”
};


/////---------tolua_runtime\macnojit\lua\lua.h
/////---------tolua_runtime\luajit-2.1\src\lua.h
#define LUA_TNONE                (-1)

#define LUA_TNIL                0
#define LUA_TBOOLEAN                1
#define LUA_TLIGHTUSERDATA        2
#define LUA_TNUMBER                3
#define LUA_TSTRING                4
#define LUA_TTABLE                5
#define LUA_TFUNCTION                6
#define LUA_TUSERDATA                7
#define LUA_TTHREAD                8


/////---------tolua_runtime\macnojit\lua\lobject.h
/////---------tolua_runtime\luajit-2.1\src\lj_obj.h
/* tags for values visible from Lua */
#define LAST_TAG        LUA_TTHREAD

#define NUM_TAGS        (LAST_TAG+1)


/*
** Extra tags for non-values
*/
#define LUA_TPROTO        (LAST_TAG+1)
#define LUA_TUPVAL        (LAST_TAG+2)
#define LUA_TDEADKEY        (LAST_TAG+3)我们知道,在C层,LIGHTUSERDATA的index为2,而数组luaT_typenames中index为2的是”userdata”。

在我看来,C层这里的”userdata”应该要改为”lightuserdata”,以便和后面的LUA_TUSERDATA 7对应的真正的”userdata”区分隔来。
我猜测,蒙占志(topameng,tolua作者) 也是这样认为,于是在C#层实现了本身的lua_typename方式,进行了上述改削,使得C#层的lua_typename方式能区分lightuserdata和userdata。

那回过头来想想,为什么lua的作者要定义lightuserdata和userdata的name都为“userdata”?
目前没有什么好的想法,猜测是有什么历史遗留问题导致的(具体是怎么样的历史遗留问题,就不清楚了)?
解决

其实可以不解决,让C/C#层按原生lua及Tolua#的实现来输出就好,知道此中的道理,有”纷歧致”这么一回事就好了。

这里也提供此外一种思路,
就是我们在C层也搞一个本身定义的lua_typename方式,区分lightuserdata和userdata,
在我们本身的东西类中使用。

代码如下:
/////---------tolua_runtime\luafixedpoint64\fp64helper.c
/////---------定义:
#ifndef UNUSED
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
#endif

static const char*const luaT_typenames_[]={
”nil”,”boolean”,”lightuserdata”,”number”,
”string”,”table”,”function”,”userdata”,”thread”,
”proto”,”upval”
};

const char *lua_typename_(lua_State *L, int t)
{
UNUSED(L);
return (t == LUA_TNONE) ? ”no value” : luaT_typenames_;
}

/////---------使用:
      const char *typeName = lua_typename_(L, lua_type(L, -1));
      result = _malloc_concatenate_spaced_strings_free_first(result, typeName ? typeName : ”nil”);
      result = _malloc_concatenate_strings_free_first(result, ”\n”);小结

开发过程中遇到了一些疑惑,发现C及C#层的lua_typename方式表示纷歧致;
然后研究一下对应的C及C#源码,测验考试理解作者的思路;
最后给出了解决方案。
(转载请注明原始出处和原作者,添加原文链接张杰全:Tolua#下C及C#层的lua_typename方式的差异)
页: [1]
查看完整版本: Tolua#下C及C#层的lua_typename方式的差异