在Lua中使用C变量和嵌套函数

时间:2012-11-21 17:45:11

标签: c variables lua nested

这是我之前关于嵌套注册C函数的问题的后续跟进: Trying to call a function in Lua with nested tables

上一个问题给了我添加这样的嵌套函数的答案:

dog.beagle.fetch()

我也想在这个级别拥有变量,如:

dog.beagle.name
dog.beagle.microchipID

我希望这个字符串和数字在C中分配并由Lua访问。因此,在C代码中,变量可以定义为:

int microchipIDNumber;
char dogname[500];

C变量需要通过Lua中的赋值进行更新,当Lua位于等号右侧时,需要由Lua检索其值。我已经尝试了__index和__newindex元方法概念,但是当我在变量的Lua路径中有2个点时,我尝试的所有内容似乎都会崩溃。我知道我可能会使2点更复杂,但它使组织更容易阅读Lua代码。我还需要获得一个事件来分配,因为我需要在microchipIDNumber值改变时启动一些硬件。我假设我可以在设置值时通过__newindex执行此操作。

关于如何编写元表和方法来完成嵌套的任何想法?可能是因为我以前的函数声明让Lua感到困惑吗?

1 个答案:

答案 0 :(得分:1)

Lua中的冒号运算符(:)仅用于函数。请考虑以下示例:

meta = {}
meta["__index"] = function(n,m) print(n) print(m) return m end
object = {}
setmetatable(object,meta)
print(object.foo)

索引函数将简单地打印它传递的两个参数并返回第二个参数(我们也将打印,因为只是执行object.foo是语法错误)。新输出的输出为table: 0x153e6d0 foo foo。所以__index获取我们正在查找变量及其名称的对象。现在,如果我们将object.foo替换为object:foo,我们就会得到:

input:5: function arguments expected near ')'

这是因为:中的object:fooobject.foo(object)的语法糖,所以Lua希望您为函数调用提供参数。如果我们确实提供了参数(object:foo("bar")),我们就会得到:

table: 0x222b3b0
foo
input:5: attempt to call method 'foo' (a string value)

所以我们的__index函数仍然被调用,但是没有传递参数 - Lua只是试图调用返回值。因此,请勿将:用于成员。

有了这个,让我们看看你如何在Lua和C之间同步变量。这实际上是非常复杂的,并且有不同的方法来实现它。一种解决方案是使用__index__newindex的组合。如果在C中有beagle结构,我建议使用这些C函数并将它们作为C-closure推送到Lua表的元表中,并将指向C结构的指针作为upvalue。有关lua_pushcclosurethis关于Lua闭包的一些信息,请查看this

如果你没有可以引用的单一结构,那么它会变得更加复杂,因为你必须以某种方式在C方面存储对variableName-variableLocation并知道它们的类型。您可以在实际的Lua表中维护这样的列表,因此dog.beagle将是一个或两个变量名称的映射。这个'某事'有几种选择。首先 - 一个轻量级用户数据(即 - 一个C指针),但是你会遇到问题指出的问题,以便你知道要为__index推送什么Lua类型以及什么到弹出__newindex。另一个选择是推送两个函数/闭包。你可以为每个你必须处理的类型(数字,字符串,表等)创建一个C函数,并为每个变量推送一个适当的函数,或者创建一个uber-closure,它接受一个参数给出的类型然后给出只是改变你推动它的上升值。在这种情况下,__index__newindex函数将只查找给定变量名称的相应函数并调用它,因此在Lua中实现它可能最容易。

对于两个函数,dog.beagle可能看起来像这样(不是实际的Lua语法):

dog.beagle = {
  __metatable = {
    __index = function(table,key)
      local getFunc = rawget(table,key).get
      return getFunc(table,key)
    end

    __newindex = function(table,key,value)
      local setFunc = rawget(table,key).set
      setFunc(table,key,value)
    end
  }
  "color" = {
    "set" = *C function for setting color or closure with an upvalue to tell it's given a color*,
    "get" = *C function for getting color or closure with an upvalue to tell it to return a color*
  }
}

关于上述内容的注释: 1.不要直接设置对象的__metatable字段 - 它用于隐藏真正的元表。使用setmetatable(object,metatable)。 2.注意rawget的用法。我们需要它,因为否则尝试从__index内获取对象的字段将是无限递归。 3.如果事件rawget(table,key)返回nil,或者返回的内容没有get / set成员,则必须进行更多错误检查。< / p>