Lua C API实现二维数组时__index元方法参数传递异常求助
问题分析与解决
你的错误核心在于对Lua索引语法的底层逻辑理解偏差:arr[i][j]在Lua中是两次独立的索引操作:
- 首先执行
arr[i],触发数组对象的__index元方法,此时Lua栈仅传入两个参数:数组userdata和索引i,不存在第三个参数j; - 随后尝试对
arr[i]的返回值执行[j]索引,但你的array2d_get函数强制要求第三个参数,直接导致参数不足的报错。
以下是两种可行的解决思路:
方案一:改用函数调用语法(简单直接)
放弃双索引语法,改用函数调用传递两个坐标参数,修改成本最低。
C代码修改
调整元表方法,新增get方法绑定原array2d_get函数,同时配置__index让对象可以直接调用方法:
#include <lauxlib.h> #include <lua.h> typedef struct Array2d { size_t height, width; int data[]; } Array2d; static int array2d_get(lua_State *L) { Array2d *arr = lua_touserdata(L, 1); size_t x = luaL_checkinteger(L, 2) - 1; size_t y = luaL_checkinteger(L, 3) - 1; luaL_argcheck(L, arr != NULL, 1, "'array' expected"); luaL_argcheck(L, x >= 0 && x < arr->width, 2, "x is not in [1,width]"); luaL_argcheck(L, y >= 0 && y < arr->height, 3, "y is not in [1,height]"); lua_pushinteger(L, arr->data[x + y * arr->width]); return 1; } static int array2d_size(lua_State *L) { Array2d *arr = lua_touserdata(L, 1); luaL_argcheck(L, arr != NULL, 1, "'array' expected"); lua_pushinteger(L, arr->width * arr->height); return 1; } static int array2d_new(lua_State *L) { size_t width = luaL_checkinteger(L, 1); size_t height = luaL_checkinteger(L, 2); size_t nbytes = sizeof(Array2d) + width * height * sizeof(int); Array2d *arr = lua_newuserdata(L, nbytes); arr->width = width; arr->height = height; luaL_getmetatable(L, "lib.arr"); lua_setmetatable(L, -2); for (int i = 0; i < width * height; i++) { arr->data[i] = 0; } return 1; } static const struct luaL_Reg libarr_f[] = {{"new", array2d_new}, {NULL, NULL}}; static const struct luaL_Reg libarr_m[] = { {"get", array2d_get}, {"__len", array2d_size}, {NULL, NULL}}; int luaopen_libarr(lua_State *L) { luaL_newmetatable(L, "lib.arr"); lua_pushvalue(L, -1); // 将元表自身设为__index,支持对象直接调用方法 lua_setfield(L, -2, "__index"); luaL_setfuncs(L, libarr_m, 0); luaL_newlib(L, libarr_f); return 1; }
Lua测试代码修改
将双索引改为函数调用:
local libarr = require "libarr" local width=3 local height=3 arr = libarr.new(width,height) for i=1,width do for j=1,height do print(arr:get(i,j)) end end
方案二:实现嵌套索引代理(贴合Lua二维数组语法)
如果想保留arr[i][j]的语法,需要让arr[i]返回一个行代理对象,该对象持有原数组和行号,再通过自身的__index元表处理列索引。
C代码修改
新增行代理结构体和元表,修改原array2d_get方法返回代理对象:
#include <lauxlib.h> #include <lua.h> typedef struct Array2d { size_t height, width; int data[]; } Array2d; // 行代理结构体,保存原数组和行号 typedef struct RowProxy { Array2d *arr; size_t row; } RowProxy; // 行代理的__index方法,处理列索引 static int rowproxy_get(lua_State *L) { RowProxy *proxy = lua_touserdata(L, 1); size_t col = luaL_checkinteger(L, 2) - 1; luaL_argcheck(L, proxy != NULL, 1, "'row proxy' expected"); luaL_argcheck(L, col >= 0 && col < proxy->arr->width, 2, "col is not in [1,width]"); lua_pushinteger(L, proxy->arr->data[col + proxy->row * proxy->arr->width]); return 1; } // 原数组的__index方法,返回行代理 static int array2d_get(lua_State *L) { Array2d *arr = lua_touserdata(L, 1); size_t row = luaL_checkinteger(L, 2) - 1; luaL_argcheck(L, arr != NULL, 1, "'array' expected"); luaL_argcheck(L, row >= 0 && row < arr->height, 2, "row is not in [1,height]"); // 创建行代理userdata RowProxy *proxy = lua_newuserdata(L, sizeof(RowProxy)); proxy->arr = arr; proxy->row = row; // 设置行代理元表 luaL_getmetatable(L, "lib.arr.row"); lua_setmetatable(L, -2); return 1; } static int array2d_size(lua_State *L) { Array2d *arr = lua_touserdata(L, 1); luaL_argcheck(L, arr != NULL, 1, "'array' expected"); lua_pushinteger(L, arr->width * arr->height); return 1; } static int array2d_new(lua_State *L) { size_t width = luaL_checkinteger(L, 1); size_t height = luaL_checkinteger(L, 2); size_t nbytes = sizeof(Array2d) + width * height * sizeof(int); Array2d *arr = lua_newuserdata(L, nbytes); arr->width = width; arr->height = height; luaL_getmetatable(L, "lib.arr"); lua_setmetatable(L, -2); for (int i = 0; i < width * height; i++) { arr->data[i] = 0; } return 1; } static const struct luaL_Reg libarr_f[] = {{"new", array2d_new}, {NULL, NULL}}; static const struct luaL_Reg libarr_m[] = { {"__index", array2d_get}, {"__len", array2d_size}, {NULL, NULL}}; static const struct luaL_Reg rowproxy_m[] = { {"__index", rowproxy_get}, {NULL, NULL}}; int luaopen_libarr(lua_State *L) { // 初始化行代理元表 luaL_newmetatable(L, "lib.arr.row"); luaL_setfuncs(L, rowproxy_m, 0); // 初始化数组元表 luaL_newmetatable(L, "lib.arr"); luaL_setfuncs(L, libarr_m, 0); luaL_newlib(L, libarr_f); return 1; }
Lua测试代码
无需修改,原arr[i][j]语法可直接正常运行:
local libarr = require "libarr" local width=3 local height=3 arr = libarr.new(width,height) for i=1,width do for j=1,height do print(arr[i][j]) end end
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

