11

我使用 Lua 5.2 的 Lua-C API 用 Lua 包装了一个 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)

我究竟做错了什么?

4

3 回答 3

11

好的,让它工作。我不得不添加__indexand__metatableFoo新的元表中,如下所示:

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");
}
于 2012-06-25T20:57:57.003 回答
4

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

您的解决方案非常好,但它不允许我想做的事情:对对象进行“类似数组”的访问并且仍然具有函数。看看这个 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]?

于 2012-07-17T00:40:23.343 回答
1

以下是我如何同时满足您和 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;
}

卢阿代码:

local Foo = require('foo')
local foo = Foo.new(12)
local bar = Foo(24)

print(Foo[13])
print(foo:doSomething())
print(bar:doSomething())

卢阿输出:

## new
## new
## index
13
## doSomething
12
## doSomething
24
## __gc
## __gc
于 2021-12-07T00:36:57.247 回答