将C整数绑定到Lua全局变量

4

在C中,我想要调用一个使用全局变量x的lua函数myFunction()。我希望能够直接将c(整数)变量注册为这个全局变量x。

大致上是这样:

demo.lua:

function myFunction()
  return x + 3
end

demo.c:

int x = 0;
lua_bindglob(L, "x", &x);

for (x = 0; x < 100; ++x)
{
  lua_getglobal(L, "myFunction");

  // here I call the function and it should evaluate
  // to the current value of x (in c)
  lua_pcall(L, 0, 1, 0);      
  int result = lua_tonumber(L, -1);  

  lua_pop(L, 1);
}

我知道可以通过以下方式修改lua全局变量:

lua_pushnumber(L, 3);
lua_setglobal(L, "x");

但我需要一种更直接/更快的方法来完成这个任务。我得到了一个提示,说LUA轻量用户数据可能会对此有帮助,但我无法找到任何关于普通变量的示例。

编辑: 由于表达式是从基准测试中获取的,因此我不想改变它们: 在此处查看示例:https://github.com/ArashPartow/math-parser-benchmark-project/blob/master/bench_expr_complete.txt


1
我不知道是否有直接实现你想要的功能的方法。几个月前我也遇到了同样的问题,最终选择使用带有元表的表/用户数据,并将__index__newindex元方法定义为访问器函数。 - Joel Cornett
我的猜测是——在Lua中,整数是一个对象,因此C代码应该能够看到并修改它的内部表示(可能不映射到32位int)。如果您曾经更改过Lua中的值,则新值将具有新地址。更好的方法是通过外部函数接口(FFI)访问C的“int”。这至少要求C代码将给定数据项导出为符号。请参考http://luajit.org/ext_ffi_tutorial.html。 - Dima Tisnek
2个回答

2

你最好能够做到的是创建一个普通的userdata对象(不是light userdata),该对象具有一个__call元方法,可为您获取/设置C值。

因此,你的函数应该类似于:

function myFunction()
  return x() + 3
end

它还可以定义运算符元方法,使得像x + 3这样的操作能够生效。但是如果只有裸露的x,就无法解析出你想要的值。

设置值的方式如下:

x(3)

无法使像 x = 3 这样的语句按照您期望的方式工作。这将始终覆盖所讨论的值。

使用 __newindex__index 元方法也不会奏效,因为您希望变量是全局的。

或者,您可以使用LuaJIT及其FFI接口。


1
即使使用LuaJIT FFI,我认为(全局)键分配也会重新绑定变量。 - Joel Cornett
3
我最终选择了LuaJIT,虽然速度较快,但仍无法击败exprTK。 - schoetbi

2
如果您只有几个C变量需要从Lua中访问,您可以在全局表上设置一个metatable,并使用__index和__newindex元方法。当访问不存在的全局变量(获取或设置)时,将调用这些元方法,并且您必须通过查看这些元方法的第二个参数(变量名)来确定要执行什么操作。
以下是导出单个变量的模块的示例代码:
#include <string.h>
#include <limits.h>
#include <lua.h>
#include <lauxlib.h>


static int x = 0;


static int myvar_index( lua_State* L ) {
  char const* key = lua_tostring( L, 2 );
  if( key != NULL && strcmp( key, "x" ) == 0 )
    lua_pushinteger( L, x );
  else
    lua_pushnil( L );
  return 1;
}


static int myvar_newindex( lua_State* L ) {
  char const* key = lua_tostring( L, 2 );
  if( key != NULL && strcmp( key, "x" ) == 0 ) {
    lua_Integer i = luaL_checkinteger( L, 3 );
    if( i > INT_MAX || i < INT_MIN )
      luaL_error( L, "variable value out of range" );
    x = i;
  } else
    lua_rawset( L, 1 );
  return 0;
}


int luaopen_myvar( lua_State* L ) {
  luaL_Reg const metamethods[] = {
    { "__index", myvar_index },
    { "__newindex", myvar_newindex },
    { NULL, NULL }
  };
#if LUA_VERSION_NUM < 502
  lua_pushvalue( L, LUA_GLOBALSINDEX );
  lua_newtable( L );
  luaL_register( L, NULL, metamethods );
#else
  lua_pushglobaltable( L );
  luaL_newlib( L, metamethods );
#endif
  lua_setmetatable( L, -2 );
  return 0;
}

如果你导出越多的 C 变量,那么元方法实现就会越复杂,使用其他建议方法可能更好。

另外需要注意的是全局表是一个共享资源,因此必须要避免/处理与修改其元表的其他代码发生冲突。(即可重复使用的模块不应该修改共享资源,但我想同样适用于 C 中的全局变量...)


这会以不良的方式影响访问全局变量的默认行为吗? - Joel Cornett
它将影响访问“未定义”的全局变量的行为。您使用的大多数全局变量具有非nil值,这些变量不会受到任何影响。访问nil值全局变量将产生额外的元方法调用。 - siffiejoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接