首先定义一个数据体
typedef struct NumArray{
int content[256];
int size = 256;
} NumArray;
然后定义各种方法。
new方法
开辟一块内存并把地址传给array。然后就可以在c++这边初始化array了。后面那一句是设置metatable的,在这里姑且先理解为找出一个叫做TYPE_LUA_ARRAY
的metatable然后设置给本userdata。
int newArray(lua_State *L){
NumArray *array = (NumArray*)lua_newuserdata(L, sizeof(NumArray));
array->size = 256;
for (auto i = 0; i != array->size; i++){
array->content[i] = 0;
}
// find out the metatable that created in the function `luaopen_array`
// which content the method 'get' and 'set'.
luaL_getmetatable(L, TYPE_LUA_ARRAY); // metatable's at the top
lua_setmetatable(L, -2);
return 1;
}
操作方法
几个点要注意一下。
- 传进来的参数必须要是
lua_State *
类型,返回必须是int类型 - 先取参数,先来的参数在靠近栈底的地方,后来的在栈顶
- 操作之后如果有返回值,把返回值放到栈顶
- 返回代表操作的返回个数
- 第一个参数一般为userdata,取出来后用lua_touserdata转成void*类型的指针,然后再转成对应的指针
int setArray(lua_State *L){
NumArray *array = (NumArray*)lua_touserdata(L, -3);
int index = lua_tointeger(L, -2);
int value = lua_tointeger(L, -1);
if (index >= (array->size - 1) || index < 0){
print_in_lua(current_lua_state, "index out of bound");
return 0;
}
array->content[index] = value;
return 0;
}
int array2string(lua_State *L){
NumArray *array = (NumArray*)lua_touserdata(L, -1);
string ret = string("array content: ");
int index = 0;
for (auto value : array->content){
if (value == 0){
continue;
}
ret = ret + to_string(value) + ",\t";
index++;
if (index >= 15){
ret = ret + "\n";
index = 0;
}
}
lua_pushstring(L, ret.c_str());
return 1;
}
int getArray(lua_State *L){
NumArray *array = (NumArray*)lua_touserdata(L, -2);
int index = lua_tointeger(L, -1);
int value = array->content[index];
lua_pushinteger(L, value);
return 1;
}
int size(lua_State *L){
NumArray *array = (NumArray*)lua_touserdata(L, -1);
lua_pushinteger(L, (int)(array->size));
return 1;
}
注册
lib
lib分为两部分,一部分为静态函数,一部分为成员函数
static const struct luaL_reg arraylib_f[] = {
{"new", newArray},
{ nullptr, nullptr },
};
static const struct luaL_reg arraylib_m[] = {
{ "set", setArray },
{ "get", getArray },
{ "size", size },
{ "__tostring", array2string },
{ nullptr, nullptr },
};
注册lib
- 首先放一个新的metatable
- 注册成员函数lib,第二个参数为nullptr代表着注册到栈顶的表里面去
- 把metatable的__index等同于get方法,把__newindex等同于set方法。这样做的好处是可以在lua中使用
t[key] = value
的方法来调用set,用t[key]
来调用get
上面一部分被我注释掉的代码可以把metatable的__index方法设置为metatable自己。
int luaopen_array(lua_State *L){
luaL_newmetatable(L, TYPE_LUA_ARRAY);
//lua_pushstring(L, "__index");
//lua_pushvalue(L, -2); // stack: (top->) metatable, "__tostring", metatable
//lua_settable(L, -3); // result: metatable.__index = metatable
// stack: (top->) metatable
luaL_openlib(L, nullptr, arraylib_m, 0); // register with the metatable at the stack top
lua_pushstring(L, "__index"); //
lua_pushstring(L, "get"); // result: (top->) "get", "__index", metatable
lua_gettable(L, -3); // result: top-> (the function)__index, "get", metatable
lua_settable(L, -3); // result: metatable.get = metatable.__index
// stack: (top->)metatable
// to make __newindex = set
lua_pushstring(L, "__newindex");
lua_pushstring(L, "set");
lua_gettable(L, -3);
lua_settable(L, -3);
luaL_openlib(L, "numarray", arraylib_f, 0);
return 0;
}
最后在lua代码里可以实现这种效果
local aArray = numarray.new(); // aAarry的类型是userdata
aArray[0] = 999;
print(aArray[0]) // output: 999
print(aArray) // automatically call the "__tostring" function