使用成员和方法进行绑定

lua-users home
wiki

描述

这是一个使用indexnewindex事件访问C结构体成员的 Lua 绑定示例。

newindex事件处理函数在它的上值中有一个查找表,其中包含可以设置的所有成员的列表。对于每个可以设置的成员,查找表包含该成员在结构体中的偏移量,以及一个指向用于设置该类型成员的胶水函数的指针。我们只需要为每种类型的成员编写一个胶水函数:doubleintchar*。这样,我们就可以对该结构体中的所有int,或暴露给 Lua 的任何其他结构体,使用相同的设置int胶水函数。

index事件处理函数类似。它有一个用于所有可以获取的成员的查找表,但它还有一个用于所有方法函数的第二个查找表。

C 代码

// CStructToLua.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
/*
==============================================================================
  Example Lua bindings with members and methods
==============================================================================
*/

static int get_int (lua_State *L, void *v)
{
  lua_pushnumber(L, *(int*)v);
  return 1;
}

static int set_int (lua_State *L, void *v)
{
  *(int*)v = luaL_checkint(L, 3);
  return 0;
}

static int get_number (lua_State *L, void *v)
{
  lua_pushnumber(L, *(lua_Number*)v);
  return 1;
}

static int set_number (lua_State *L, void *v)
{
  *(lua_Number*)v = luaL_checknumber(L, 3);
  return 0;
}

static int get_string (lua_State *L, void *v)
{
  lua_pushstring(L, (char*)v );
  return 1;
}

typedef int (*Xet_func) (lua_State *L, void *v);

/* member info for get and set handlers */
typedef const struct{
  const char *name;  /* member name */
  Xet_func func;     /* get or set function for type of member */
  size_t offset;     /* offset of member within your_t */
}  Xet_reg_pre;

typedef Xet_reg_pre * Xet_reg;

static void Xet_add (lua_State *L, Xet_reg l)
{
  for (; l->name; l++) {
    lua_pushstring(L, l->name);
    lua_pushlightuserdata(L, (void*)l);
    lua_settable(L, -3);
  }
}

static int Xet_call (lua_State *L)
{
  /* for get: stack has userdata, index, lightuserdata */
  /* for set: stack has userdata, index, value, lightuserdata */
  Xet_reg m = (Xet_reg)lua_touserdata(L, -1);  /* member info */
  lua_pop(L, 1);                               /* drop lightuserdata */
  luaL_checktype(L, 1, LUA_TUSERDATA);
  return m->func(L, (void *)((char *)lua_touserdata(L, 1) + m->offset));
}

static int index_handler (lua_State *L)
{
  /* stack has userdata, index */
  lua_pushvalue(L, 2);                     /* dup index */
  lua_rawget(L, lua_upvalueindex(1));      /* lookup member by name */
  if (!lua_islightuserdata(L, -1)) {
    lua_pop(L, 1);                         /* drop value */
    lua_pushvalue(L, 2);                   /* dup index */
    lua_gettable(L, lua_upvalueindex(2));  /* else try methods */
    if (lua_isnil(L, -1))                  /* invalid member */
      luaL_error(L, "cannot get member '%s'", lua_tostring(L, 2));
    return 1;
  }
  return Xet_call(L);                      /* call get function */
}

static int newindex_handler (lua_State *L)
{
  /* stack has userdata, index, value */
  lua_pushvalue(L, 2);                     /* dup index */
  lua_rawget(L, lua_upvalueindex(1));      /* lookup member by name */
  if (!lua_islightuserdata(L, -1))         /* invalid member */
    luaL_error(L, "cannot set member '%s'", lua_tostring(L, 2));
  return Xet_call(L);                      /* call set function */
}

/*
==============================================================================
  Your structure and custom methods
==============================================================================
*/

#define YOUR_T "YourClass"

typedef struct {
  int    id;
  char   name[16];
  int    age;
  double x,y;
} your_t;


static your_t *check_your_t (lua_State *L, int index)
{
  your_t *yd;
  luaL_checktype(L, index, LUA_TUSERDATA);
  yd = (your_t *)luaL_checkudata(L, index, YOUR_T);
  if (yd == NULL) luaL_typerror(L, index, YOUR_T);
  return yd;
}

static your_t *push_your_t (lua_State *L)
{
  your_t *yd = (your_t*)lua_newuserdata(L, sizeof(your_t));
  luaL_getmetatable(L, YOUR_T);
  lua_setmetatable(L, -2);
  return yd;
}

static int id_counter;

static int your_create (lua_State *L)
{
  your_t *yd;
  unsigned int  name_len;
  const char *name = luaL_checklstring(L, 1, &name_len);
  if( name_len > 15 ) luaL_error(L, "name too long"); /* die */
  yd = push_your_t(L);
  strcpy( yd->name, name );
  yd->age = luaL_checkint(L, 2);
  yd->x   = luaL_checknumber(L, 3);
  yd->y   = luaL_checknumber(L, 4);
  yd->id  = ++id_counter;
  return 1;
}

static int your_destroy (lua_State *L)
{
  your_t *yd = (your_t*)lua_touserdata(L,1);
  printf("Goodbye %s:%d at (%lf,%lf)\n", yd->name, yd->id, yd->x, yd->y);
  return 0;
}

static int your_position (lua_State *L)
{
  your_t *yd = check_your_t(L, 1);
  double   x = yd->x;
  double   y = yd->y;
  if( lua_gettop(L) > 1 ) {
    yd->x = luaL_checknumber(L, 2);
    yd->y = luaL_checknumber(L, 3);
  }
  lua_pushnumber(L,x);
  lua_pushnumber(L,y);
  return 2;
}

static int test (lua_State *L)
{
  int n = luaL_checknumber(L, 1);
  lua_pushnumber(L, 66);
  lua_pushnumber(L, 67);
  lua_pushnumber(L, 68);
  return n;
}


static const luaL_reg your_meta_methods[] = {
{"__gc", your_destroy },
{0,0}
};

static const luaL_reg your_methods[] = {
{"create",   your_create},
{"position", your_position},
{"test",     test},
{0,0}
};

static const Xet_reg_pre your_getters[] = {
{"id",   get_int,    offsetof(your_t,id)   },
{"name", get_string, offsetof(your_t,name) },
{"age",  get_int,    offsetof(your_t,age)  },
{"x",    get_number, offsetof(your_t,x)    },
{"y",    get_number, offsetof(your_t,y)    },
{0,0}
};

static const Xet_reg_pre your_setters[] = {
{"age",  set_int,    offsetof(your_t,age)  },
{"x",    set_number, offsetof(your_t,x)    },
{"y",    set_number, offsetof(your_t,y)    },
{0,0}
};


int your_register (lua_State *L)
{
  int metatable, methods;

  /* create methods table, & add it to the table of globals */
  luaL_openlib(L, YOUR_T, your_methods, 0);
  methods = lua_gettop(L);

  /* create metatable for your_t, & add it to the registry */
  luaL_newmetatable(L, YOUR_T);
  luaL_openlib(L, 0, your_meta_methods, 0);  /* fill metatable */
  metatable = lua_gettop(L);

  lua_pushliteral(L, "__metatable");
  lua_pushvalue(L, methods);    /* dup methods table*/
  lua_rawset(L, metatable);     /* hide metatable:
                                   metatable.__metatable = methods */
  lua_pushliteral(L, "__index");
  lua_pushvalue(L, metatable);  /* upvalue index 1 */
  Xet_add(L, your_getters);     /* fill metatable with getters */
  lua_pushvalue(L, methods);    /* upvalue index 2 */
  lua_pushcclosure(L, index_handler, 2);
  lua_rawset(L, metatable);     /* metatable.__index = index_handler */

  lua_pushliteral(L, "__newindex");
  lua_newtable(L);              /* table for members you can set */
  Xet_add(L, your_setters);     /* fill with setters */
  lua_pushcclosure(L, newindex_handler, 1);
  lua_rawset(L, metatable);     /* metatable.__newindex = newindex_handler */

  lua_pop(L, 1);                /* drop metatable */
  return 1;                     /* return methods on the stack */
}


int main(int argc, char *argv[])
{
  lua_State *L = lua_open();

  luaopen_base(L);
  luaopen_table(L);
  //luaopen_io(L);
  luaopen_string(L);
  luaopen_math(L);
  luaopen_debug(L);

  your_register(L);

	luaL_dofile(L, "test.lua");
  //if(argc>1) lua_dofile(L, argv[1]);

  lua_close(L);
  return 0;
}

编译代码

此代码可以按如下方式编译用于 Lua 5.0

gcc member.c  -L/usr/local/lib/ -llua -llualib

Lua 测试代码

print('= YourClass =',YourClass)
for n,v in YourClass do print(n,v) end

b = YourClass.create('bill', 99, 34,65)
f = YourClass.create('fish', 44, 16.7,19.25)

local member_names = {'id','name','age','x','y'}

function dump(ud)
  for _,n in pairs(member_names) do print(n, ud[n]) end
end

print'*** b ***'
dump(b)

print'*** f ***'
dump(f)

print('b.name =', b.name, 'pos = ', b.x, b.y)
b.age = 88
b.x = 40
b.y = 50

print'*** modified b ***'
dump(b)

debug.debug()

测试代码输出

$ ./a member.lua
= YourClass =   table: 0xa044f00
test    function: 0xa044f60
create  function: 0xa044f28
position        function: 0xa045240
*** b ***
id      1
name    bill
age     99
x       34
y       65
*** f ***
id      2
name    fish
age     44
x       16.7
y       19.25
b.name =        bill    pos =   34      65
*** modified b ***
id      1
name    bill
age     88
x       40
y       50
lua_debug> print(b.age, b:position())
88      40      50
lua_debug> b.age = 99
lua_debug> b.name = 'rat'
[string "b.name = 'rat'..."]:1: cannot set member 'name'
lua_debug> print(b.cow)
[string "print(b.cow)..."]:1: cannot get member 'cow'
lua_debug> b.fish = 9
[string "b.fish = 9..."]:1: cannot set member 'fish'
lua_debug> dump(b)
id      1
name    bill
age     99
x       40
y       50
lua_debug> cont
Goodbye fish:2 at (16.700000,19.250000)
Goodbye bill:1 at (40.000000,50.000000)

最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2008 年 8 月 5 日凌晨 4:09 GMT (差异)