Passing 1D Arrays from C/C++ to Lua Using Userdata with Metatables

发布于:2025-04-18 ⋅ 阅读:(23) ⋅ 点赞:(0)

Passing 1D Arrays from C/C++ to Lua Using Userdata with Metatables

To pass a 1D array from C/C++ to Lua while maintaining type safety and providing array-like access, you can use Lua userdata with metatables. Here’s a comprehensive approach:

1. Basic Implementation

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

typedef struct {
    size_t size;
    double* data;  // Using double as example, can be any type
} Array;

// Metatable name for our array type
static const char* ARRAY_MT = "ArrayMT";

// Create a new array and push it as userdata to Lua stack
static int new_array(lua_State* L) {
    size_t size = luaL_checkinteger(L, 1);
    
    // Allocate userdata and array structure
    Array* arr = (Array*)lua_newuserdata(L, sizeof(Array));
    arr->size = size;
    arr->data = (double*)malloc(size * sizeof(double));
    
    // Initialize array (optional)
    for (size_t i = 0; i < size; i++) {
        arr->data[i] = 0.0;
    }
    
    // Set metatable
    luaL_getmetatable(L, ARRAY_MT);
    lua_setmetatable(L, -2);
    
    return 1;
}

// Index operation (array.get or array[index])
static int array_index(lua_State* L) {
    Array* arr = (Array*)luaL_checkudata(L, 1, ARRAY_MT);
    
    if (lua_isinteger(L, 2)) {
        // Array access (1-based index in Lua)
        lua_Integer idx = lua_tointeger(L, 2);
        if (idx < 1 || idx > (lua_Integer)arr->size) {
            return luaL_error(L, "array index out of bounds");
        }
        lua_pushnumber(L, arr->data[idx - 1]);
        return 1;
    }
    
    // Else try to access a method
    lua_getmetatable(L, 1);
    lua_pushvalue(L, 2);
    lua_rawget(L, -2);
    
    return 1;
}

// Newindex operation (array[index] = value)
static int array_newindex(lua_State* L) {
    Array* arr = (Array*)luaL_checkudata(L, 1, ARRAY_MT);
    lua_Integer idx = luaL_checkinteger(L, 2);
    double value = luaL_checknumber(L, 3);
    
    if (idx < 1 || idx > (lua_Integer)arr->size) {
        return luaL_error(L, "array index out of bounds");
    }
    
    arr->data[idx - 1] = value;
    return 0;
}

// Get array size
static int array_size(lua_State* L) {
    Array* arr = (Array*)luaL_checkudata(L, 1, ARRAY_MT);
    lua_pushinteger(L, arr->size);
    return 1;
}

// GC method to free memory
static int array_gc(lua_State* L) {
    Array* arr = (Array*)luaL_checkudata(L, 1, ARRAY_MT);
    free(arr->data);
    return 0;
}

// Register the array type
static void register_array(lua_State* L) {
    static const luaL_Reg array_metamethods[] = {
        {"__index", array_index},
        {"__newindex", array_newindex},
        {"__len", array_size},
        {"__gc", array_gc},
        {NULL, NULL}
    };
    
    static const luaL_Reg array_methods[] = {
        {"size", array_size},
        {NULL, NULL}
    };
    
    // Create metatable
    luaL_newmetatable(L, ARRAY_MT);
    
    // Set metamethods
    luaL_setfuncs(L, array_metamethods, 0);
    
    // Add methods to metatable
    luaL_newlib(L, array_methods);
    lua_setfield(L, -2, "__index");
    
    // Pop metatable from stack
    lua_pop(L, 1);
}

// Module registration
int luaopen_array(lua_State* L) {
    register_array(L);
    
    static const luaL_Reg lib[] = {
        {"new", new_array},
        {NULL, NULL}
    };
    
    luaL_newlib(L, lib);
    return 1;
}

2. Usage in Lua

local array = require("array")

-- Create a new array of size 5
local arr = array.new(5)

-- Set values (1-based indexing)
arr[1] = 10.5
arr[2] = 20.3
arr[3] = 30.7

-- Get values
print(arr[1])  --> 10.5
print(arr[2])  --> 20.3

-- Get size
print(#arr)    --> 5
print(arr:size()) --> 5

3. Enhanced Version with Type Safety

For better type safety and support for different data types, you can:

  1. Create separate metatables for different array types
  2. Add bounds checking
  3. Implement more array operations (slice, map, etc.)

4. Passing Existing C Arrays

If you want to wrap an existing C array without copying:

static int wrap_array(lua_State* L) {
    double* data = (double*)lua_touserdata(L, 1);
    size_t size = luaL_checkinteger(L, 2);
    
    Array* arr = (Array*)lua_newuserdata(L, sizeof(Array));
    arr->size = size;
    arr->data = data;  // Use existing array
    
    luaL_getmetatable(L, ARRAY_MT);
    lua_setmetatable(L, -2);
    
    return 1;
}

Key Points

  1. Userdata holds both the array pointer and size information
  2. Metatables provide object-oriented access and operator overloading
  3. __gc ensures proper cleanup of allocated memory
  4. 1-based indexing matches Lua conventions
  5. The implementation can be extended to support more features

Remember to compile this as a shared library and load it in Lua using require.


网站公告

今日签到

点亮在社区的每一天
去签到