【问题标题】:How do I create a class object in Lua-C API 5.2?如何在 Lua-C API 5.2 中创建类对象?
【发布时间】:2022-01-15 04:46:37
【问题描述】:

我正在使用 Lua 5.2 的 Lua-C API 包装一个 C 函数:

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>

int foo_gc();
int foo_index();
int foo_newindex();
int foo_dosomething();
int foo_new();

struct foo {
  int x;
};

static const luaL_Reg _meta[] = {
    {"__gc", foo_gc},
    {"__index", foo_index},
    {"__newindex", foo_newindex},
    { NULL, NULL }
};
static const luaL_Reg _methods[] = {
    {"new", foo_new},
    {"dosomething", foo_dosomething},
    { NULL, NULL }
};

int foo_gc(lua_State* L) {
  printf("## __gc\n");
  return 0;
}
int foo_newindex(lua_State* L) {
  printf("## __newindex\n");
  return 0;
}
int foo_index(lua_State* L) {
  printf("## __index\n");
  return 0;
}
int foo_dosomething(lua_State* L) {
  printf("## dosomething\n");
  return 0;
}
int foo_new(lua_State* L) {
  printf("## new\n");

  lua_newuserdata(L,sizeof(Foo));
  luaL_getmetatable(L, "Foo");
    lua_setmetatable(L, -2); 

  return 1;
}

void register_foo_class(lua_State* L) {
    luaL_newlib(L, _methods); 
  luaL_newmetatable(L, "Foo");
  luaL_setfuncs(L, _meta, 0);
  lua_setmetatable(L, -2);
  lua_setglobal(L, "Foo");
}

当我运行这个 Lua 时:

local foo = Foo.new()
foo:dosomething()

...我看到这个输出(有错误):

## new
## __index
Failed to run script: script.lua:2: attempt to call method 'dosomething' (a nil value)

我做错了什么?

【问题讨论】:

    标签: lua


    【解决方案1】:

    好的,开始工作了。我不得不将__index__metatable添加到Foo的新元表中,如下图:

    void register_foo_class(lua_State* L) {
      int lib_id, meta_id;
    
      /* newclass = {} */
      lua_createtable(L, 0, 0);
      lib_id = lua_gettop(L);
    
      /* metatable = {} */
      luaL_newmetatable(L, "Foo");
      meta_id = lua_gettop(L);
      luaL_setfuncs(L, _meta, 0);
    
      /* metatable.__index = _methods */
      luaL_newlib(L, _methods);
      lua_setfield(L, meta_id, "__index");  
    
      /* metatable.__metatable = _meta */
      luaL_newlib(L, _meta);
      lua_setfield(L, meta_id, "__metatable");
    
      /* class.__metatable = metatable */
      lua_setmetatable(L, lib_id);
    
      /* _G["Foo"] = newclass */
      lua_setglobal(L, "Foo");
    }
    

    【讨论】:

      【解决方案2】:

      我尝试回复您的解决方案,但显然我还没有这样做的声誉,所以这里有一个单独的答案。

      您的解决方案非常好,但它不允许我想做的事情:对对象具有“类似数组”的访问权限并且仍然具有函数。看看这个 Lua 代码:

      Foo = {}
      
      mt = {
      __index = function(table, key)
        print("Accessing array index ", tostring(key), "\n")
        return 42
      end
      }
      setmetatable(Foo, mt)
      
      Foo.bar = function()
        return 43
      end
      
      print(tostring(Foo[13]), "\n")
      print(tostring(Foo.bar()), "\n")
      
      --[[
      Output:
      Accessing array index 13
      42
      43
      ]]--
      

      使用您的解决方案注册课程似乎不允许这样做,因为__index 条目已被覆盖。 在一个类上同时拥有数组访问和函数访问可能没有意义,但为了简单起见(提供一个 C 函数来注册这两种类型的类),我想在任何地方都使用相同的代码。有谁知道如何绕过这个限制,以便我可以从 C 创建一个类,它既有函数 Foo.bar() 又有 Foo[13]?

      【讨论】:

        【解决方案3】:

        这就是我如何同时满足您和 j_schultz 的标准

        #include <lua.h>
        #include <lauxlib.h>
        #include <stdlib.h>
        #include <stdio.h>
        
        #define LUA_FOO "Foo"
        
        typedef struct {
            int x;
        } Foo;
        
        static int foo_gc(lua_State *L) {
            printf("## __gc\n");
            Foo *foo = *(Foo**)luaL_checkudata(L, 1, LUA_FOO);
            free(foo);
            return 0;
        }
        
        static int foo_doSomething(lua_State *L) {
            printf("## doSomething\n");
            Foo *foo = *(Foo**)luaL_checkudata(L, 1, LUA_FOO);
            lua_pushinteger(L, foo->x);
            return 1;
        }
        
        static int foo_new(lua_State* L) {
            printf("## new\n");
            Foo *foo = malloc(sizeof(Foo));
            int i = 1 + lua_istable(L, 1);
            foo->x = !lua_isnoneornil(L, i) ? luaL_checkinteger(L, i) : 0;
            *(Foo**)lua_newuserdata(L, sizeof(Foo*)) = foo;
            luaL_setmetatable(L, LUA_FOO);
            return 1;
        }
        
        static int foo_index(lua_State *L) {
            printf("## index\n");
            int i = luaL_checkinteger(L, 2);
            lua_pushinteger(L, i);
            return 1;
        }
        
        int luaopen_foo(lua_State *L) {
            // instance functions
            static const luaL_Reg meta[] =
            {   { "__gc"        ,foo_gc          },
                { NULL          ,NULL            }  };
            static const luaL_Reg meth[] =
            {   { "doSomething" ,foo_doSomething },
                { NULL          ,NULL            }  };
            luaL_newmetatable(L, LUA_FOO);
            luaL_setfuncs    (L, meta, 0);
            luaL_newlib      (L, meth);
            lua_setfield     (L, -2, "__index");
            lua_pop          (L, 1);
        
            // static functions
            static const luaL_Reg static_meta[] =
            {   { "__index" ,foo_index },
                { "__call"  ,foo_new   },
                { NULL      ,NULL      }  };
            static const luaL_Reg static_meth[] =
            {   { "new"     ,foo_new   },
                { NULL      ,NULL      }  };
            luaL_newlib      (L, static_meth);
            luaL_newlib      (L, static_meta);
            lua_setmetatable (L, -2);
            return 1;
        }
        

        Lua 代码:

        local Foo = require('foo')
        local foo = Foo.new(12)
        local bar = Foo(24)
        
        print(Foo[13])
        print(foo:doSomething())
        print(bar:doSomething())
        

        Lua 输出:

        ## new
        ## new
        ## index
        13
        ## doSomething
        12
        ## doSomething
        24
        ## __gc
        ## __gc
        

        【讨论】:

        • 我正在尝试运行您的示例,但是您如何让require('foo') 调用luaopen_foo?如果您可以在示例中添加一个简单的 main() 以使其可执行,那就太好了。
        • @erwin 我所做的是构建 C 代码以生成共享对象(.so 或 .dll),然后运行 ​​lua 脚本(确保脚本的名称不是 foo.lua)。 Lua 将搜索一个名为 foo.sofoo.dll 的共享对象,如果找到它,它将在其中查找一个名为 luaopen_&lt;the_name_of_the_library&gt; 的特定命名函数
        猜你喜欢
        • 2013-01-15
        • 2018-04-16
        • 1970-01-01
        • 2015-02-07
        • 1970-01-01
        • 2012-12-26
        • 1970-01-01
        • 2019-09-10
        • 1970-01-01
        相关资源
        最近更新 更多