从Lua修改main中的C ++数组,无需额外分配

问题描述

我正在草绘一个小的C ++程序,该程序会将数组传递给Lua并在那里进行修改,我打算在其中读取lua脚本,以便无需重新编译程序就可以对其进行修改

我的第一个障碍是确保Lua能够修改已经分配的数组,而不是在Lua空间中再次分配它们。数据将是浮动的,并且大小将非常大,但是我现在从小开始。

为简化此界面,我尝试了LuaBridge 2.6,但未提供预期的结果。下面是一个完全“有效”的程序。

assignOneTrack()

请注意,没有任何匹配项。 Lua中output [10]的内容不是C ++空间中input [256]的值,而是input [0]。 C ++输出数组未在Lua中更新,cout表明它在我们初始化时保持不变(0)。 为了确认这一点,我们将output [10]的这个值压入了栈,在C ++中不是input [256],然后从C ++中检索到。 你们能纠正我还是指出我应该在哪里实现这一目标?

=======更新2020年8月11日=======

为弄清楚程序在做什么(或应该做什么),在阅读了Robert和Joseph的注意事项之后,我在C ++部分及其调用的lua脚本的更新版本下面发布了内容。注意,由于我第一次尝试没有成功,所以我放弃了LuaBridge:

C ++:

#include <iostream>
#include <cstdint>
#include <cstring>
#include <vector>
#include <lua5.3/lua.hpp>
#include <LuaBridge/LuaBridge.h>

int main(void)
    {
    const uint32_t      LENGTH = 512 * 256;
    std::vector <float> input(LENGTH),output(LENGTH);

    memset(output.data(),LENGTH * sizeof(float));   // Zero the output
    for(uint32_t i = 0; i < LENGTH; i++)                // Populate input
        input[i] = (float)i + 0.5f;

    lua_State *luastate = luaL_newstate();
    luabridge::push(luastate,input.data());    // Supposedly passing a pointer to the first element of input,according to LuaBridge manual chap 3-3.1
    luabridge::push(luastate,output.data());   // Same for output

    luaL_dostring(luastate,"output[10] = input[256]");     // Expecting to assign this value in the C++ arrays,not in the Lua space
    lua_getglobal(luastate,"output[10]");                  // Find this assigned value in the Lua stack
    lua_Number val = lua_tonumber(luastate,1);             // Retrieving this value from Lua to C++

    std::cout << input[256] << "," << output[10] << "," << val << std::endl;  // The values of val and in output[10] don't match

    lua_close(luastate);

    return 0;
    }

Lua脚本如下:

#include <iostream>
#include <cstdint>
#include <cstring>
#include <vector>
#include <luajit-2.0/lua.hpp>  // LuaJIT 2.0.4 from Ubuntu 16.04

int main(void)
    {
    const uint32_t      LENGTH = 256 * 512;
    std::vector <float> input(LENGTH),LENGTH * sizeof(float));
    for(uint32_t i = 0; i < LENGTH; i++)
        input[i] = (float)i + 0.5f;

    lua_State *luastate = luaL_newstate();
    luaL_openlibs(luastate);

    // Here I have to pass &input[0],&output[0] and LENGTH
    // to Lua,which in turn will pass to whatever functions
    // are being called from a .so lib opened in Lua-side

    luaL_dofile(luastate,"my_script.lua");    
    lua_close(luastate);

    return 0;
    }

解决方法

我正在草绘一个小的C ++程序,该程序会将数组传递给Lua

数据将是浮动的,并且大小将非常大,

我的建议:

  • 将缓冲区保留在C侧(例如,作为全局变量)
  • 将C函数暴露给LUA GetTableValue(Index)
  • 将C函数暴露给Lua SetTableValue(Index,Value)

应该是这样的:

static int LUA_GetTableValue (lua_State *LuaState)
{
  float Value;

  /* lua_gettop returns the number of arguments */
  if ((lua_gettop(LuaState) == 1) && (lua_isinteger(LuaState,-1)))
  {
    /* Get event string to execute (first parameter) */
    Offset = lua_tointeger(LuaState,-1);

    /* Get table value */
    Value  = LUA_FloatTable[Offset];

    /* Push result to the stack */
    lua_pushnumber(Value);
  }
  else
  {
    lua_pushnil(LuaState);  
  }

  /* return 1 value */
  return 1;
}

您还需要注册该功能:

lua_register(LuaState,"GetTableValue",LUA_GetTableValue);

我让你写SetTableValue,但是它应该非常接近。 这样,缓冲区位于C端,可以使用专用功能从Lua进行访问。

,

我建议您创建一个用户数据,以通过__index__newindex公开数组,就像这样(像Lua一样,用C和C ++多种语言编写):

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

#ifdef __cplusplus
extern "C" {
#endif
#include <lua5.3/lua.h>
#include <lua5.3/lauxlib.h>
#ifdef __cplusplus
}
#endif

struct MyNumbers {
    lua_Number *arr;
    lua_Integer len;
};

int MyNumbers_index(lua_State *L) {
    struct MyNumbers *t = (struct MyNumbers *)luaL_checkudata(L,1,"MyNumbers");
    lua_Integer k = luaL_checkinteger(L,2);
    if(k >= 0 && k < t->len) {
        lua_pushnumber(L,t->arr[k]);
    } else {
        lua_pushnil(L);
    }
    return 1;
}

int MyNumbers_newindex(lua_State *L) {
    struct MyNumbers *t = (struct MyNumbers *)luaL_checkudata(L,2);
    if(k >= 0 && k < t->len) {
        t->arr[k] = luaL_checknumber(L,3);
        return 0;
    } else {
        return luaL_argerror(L,2,lua_pushfstring(L,"index %d out of range",k));
    }
}

struct MyNumbers *MyNumbers_new(lua_State *L,lua_Number *arr,lua_Integer len) {
    struct MyNumbers *var = (struct MyNumbers *)lua_newuserdata(L,sizeof *var);
    var->arr = arr;
    var->len = len;
    luaL_setmetatable(L,"MyNumbers");
    return var;
}

int main(void) {
    const lua_Integer LENGTH = 512 * 256;
    lua_Number input[LENGTH],output[LENGTH];

    memset(output,sizeof output);
    for(lua_Integer i = 0; i < LENGTH; ++i)
        input[i] = i + 0.5f;

    lua_State *L = luaL_newstate();

    luaL_newmetatable(L,"MyNumbers");
    lua_pushcfunction(L,MyNumbers_index);
    lua_setfield(L,-2,"__index");
    lua_pushcfunction(L,MyNumbers_newindex);
    lua_setfield(L,"__newindex");
    /* exercise for the reader: implement __len and __pairs too,and maybe shift the indices so they're 1-based to Lua */
    lua_pop(L,1);

    MyNumbers_new(L,input,LENGTH);
    lua_setglobal(L,"input");
    MyNumbers_new(L,output,"output");

    luaL_dostring(L,"output[10] = input[256]");
    lua_getglobal(L,"output");
    lua_geti(L,-1,10);
    lua_Number val = lua_tonumber(L,-1);

    printf("%f,%f,%f\n",input[256],output[10],val);

    lua_close(L);
}

使用这种方法,Lua中没有任何数据的副本,而您自己的MyNumbers_函数控制着如何完成对它们的所有访问。


如果您希望能够通过LuaJIT的FFI使用数组,而不是直接在Lua中操作它们,那么可以将它们的地址传递给一个简单的用户数据,如下所示:

#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif
#include <luajit-2.0/lua.h>
#include <luajit-2.0/lualib.h>
#include <luajit-2.0/lauxlib.h>
#ifdef __cplusplus
}
#endif

int main(void) {
    const lua_Integer LENGTH = 256 * 512;
    lua_Number input[LENGTH],sizeof output);
    for(lua_Integer i = 0; i < LENGTH; ++i)
        input[i] = i + 0.5f;

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    lua_pushlightuserdata(L,input);
    lua_setglobal(L,"input");
    lua_pushlightuserdata(L,output);
    lua_setglobal(L,"output");
    lua_pushinteger(L,"LENGTH");

    luaL_dofile(L,"my_script.lua");
    lua_close(L);
}