【问题标题】:Query Lua userdata type from C从 C 中查询 Lua 用户数据类型
【发布时间】:2010-10-18 03:18:14
【问题描述】:

我有一个具有特定元表类型的 Lua 用户数据对象(例如 "stackoverflow.test")。从 C 代码中,我希望能够准确检查它是哪种类型,并根据结果表现出不同的行为。是否有一个很好的方便功能(类似于luaL_checkudata,但如果答案不是您想要的,则不会出错)让我查询用户数据的元表类型名称?如果没有,我想我需要使用lua_getmetatable,但是我有点不清楚如何确定刚刚添加到堆栈中的元表的名称。

澄清一下:我使用的是 Lua 5.1,其中 luaL_checkudata 的行为发生了变化。我知道在 5.0 中它不会出错。

【问题讨论】:

标签: types lua


【解决方案1】:

我刚刚查看了luaL_checkudata 函数的源代码,它基本上使用lua_getmetatable 获取用户数据对象的元表。然后它使用lua_getfield 从注册表中获取给定的类型名称,并调用lua_rawequal 来比较它们。

【讨论】:

    【解决方案2】:

    您始终可以在元表中存储一个标记字段,其中包含您的模块独有的轻量级用户数据值。

    static const char *green_flavor = "green";
    ...
    void my_setflavor(lua_State *L, void *flavor) {
      lua_pushlightuserdata(L,flavor);
      lua_pushlstring(L,"_flavor");
      lua_rawset(L,-3);
    }
    
    void my_isflavor(lua_State *L, void *flavor) {
      void *p = NULL;
      lua_pushlstring(L,"_flavor");
      lua_rawget(L,-2);
      p = lua_touserdata(L,-1);
      lua_pop(L,1);
      return p == flavor;
    }
    

    然后可以使用my_setflavor(L,&green_flavor)设置栈顶表的_flavor字段,my_isflavor(L,&red_flavor)测试栈顶表的_flavor字段。

    使用这种方式,_flavor 字段只能采用可以由模块中具有符号 green_flavor 的代码创建的值,并且查找该字段并测试其值只需要一次表查找,除了检索元表本身。请注意,变量 green_flavor 的值无关紧要,因为实际使用的只是它的地址。

    有几个不同的风味变量可用作标记值,_flavor 字段可用于区分几个相关的元表。

    综上所述,一个自然的问题是“为什么要这样做?”毕竟,元表可以很容易地包含获得适当行为所需的所有信息。它可以很容易地保存函数和数据,并且可以从 C 和 Lua 中检索和调用这些函数。

    【讨论】:

      【解决方案3】:

      用户数据必须有一个元表,所以抓住它;然后在注册表中查找您想要的名称。如果这两个对象相同,则您已找到所需的类型。

      您可以在 C 代码中对这种类型进行调度,但请允许我建议您改为指定元表的一个字段。存储在元表中的函数应该可以完成这项工作,但如果不是,如果您绝对必须在 C 代码中使用 switch,然后选择一个名称,使用它来索引元表,并为每个元表分配一个可以切换的小整数开。

      meta1.decision = 1
      meta2.decision = 2
      meta3.decision = 3
      

      然后在你的 C 代码中

      if (lua_getmetatable(L, 1)) {
        lua_getfield(L, -1, "decision");
        if (lua_isnumber(L, -1)) {
          switch ((int) lua_tonumber(L, -1)) {
             case 1: ... ; break;
             case 2: ... ; break;
             case 3: ... ; break;
          }
          return 0;
        }
      }
      return luaL_error(L, "Userdata was not one of the expected types");
      

      【讨论】:

        【解决方案4】:

        您将使用lua_getmetatablelua_equal 来测试表是否相同。

        在我看来,Lua 应该对这种类型扩展的东西给予更多的支持。到目前为止,这确实是 Lua/C(++) 包装系统的责任。

        在我最近完成的包装器中(作为商业项目的一部分),我使用class::instance(L,index) 来获取特定类型的用户数据指针。换句话说,该方法检查它是用户数据并且元表也是正确的。如果不是,则返回 NULL。

        Lua 可以帮助这一切的方式是,如果元表有一个标准字段用于扩展类型信息(s.a.__type)。这可以用来使type() 本身返回“userdata”、“xxx”(两个值,当前只返回一个)。这将与大多数当前代码保持兼容。但这只是假设(除非您执行自定义 type() 并自行实现)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-08-31
          • 1970-01-01
          • 1970-01-01
          • 2013-07-02
          • 2015-04-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多