You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lua C API实现二维数组时__index元方法参数传递异常求助

问题分析与解决

你的错误核心在于对Lua索引语法的底层逻辑理解偏差:arr[i][j]在Lua中是两次独立的索引操作:

  1. 首先执行arr[i],触发数组对象的__index元方法,此时Lua栈仅传入两个参数:数组userdata和索引i,不存在第三个参数j;
  2. 随后尝试对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:11:08