如何获取从 C++ 发送到 Lua 函数的 table 的更新值?
How to get updated value of table sent from C++ to Lua function?
我正在尝试将浮点矢量从 C++ 函数传递给 Lua 函数作为 table 参数,然后在 Lua 函数调用后获取矢量的更新值.
这是简单的示例代码。
void myFunc(lua_State *L, std::vector<float> &vec) {
lua_getglobal(L, "myFunc");
lua_newtable(L);
for (size_t i=0; i<vec.size(); ++i) {
lua_pushinteger(L, i+1);
lua_pushnumber(L, vec[i]);
lua_settable(L, -3);
}
if (lua_pcall(L, 1, 0, 0) != 0) {
std::cout << "Error : Failed to call myFunc" << std::endl;
}
}
然后我可以调用这个函数如下。
std::vector<float> vec = {1,2,3,4,5}; //an array that will be sent to Lua as table
myFunc(L, vec); //call "myFunc" function in Lua and pass the array as an argument
/* <Lua function which will be called>
function myFunc(t)
for i=1, #t do
t[i] = t[i] * 2
end
end
*/
//how to update elements of "vec" here so it now becomes {2,4,6,8,10}?
正如我在代码中评论的那样,我想在调用 Lua 函数后更新 vector<float> vec
的元素。
是否可以将数组传递给Lua函数作为参考? (就像它在 C++ 函数中的工作方式一样)
如果不是,是否可以获取 Lua table(t) 的值,以便我可以在调用函数后将它们写回 C++ 中的浮点向量?
谢谢!
将 std::vector 转换为 Lua table
正如所讨论的那样 in chat 可能需要将函数参数从 std::vector<float>
转换为 Lua table 并将 return 值从 Lua table 到 std::vector<float>
。这样做的好处是Lua端完全透明
函数as_table
从一对迭代器创建一个新的table,from_table
从栈顶的Luatable转换为一对迭代器。
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
#include <lua.hpp>
template <typename T, typename U>
void as_table(lua_State* L, T begin, U end) {
lua_newtable(L);
for (size_t i = 0; begin != end; ++begin, ++i) {
lua_pushinteger(L, i + 1);
lua_pushnumber(L, *begin);
lua_settable(L, -3);
}
}
template <typename T, typename U>
void from_table(lua_State* L, T begin, U end) {
assert(lua_istable(L,-1));
for (size_t i = 0; begin != end; ++begin, ++i) {
lua_pushinteger(L, i + 1);
lua_gettable(L, -2);
*begin = lua_tonumber(L, -1);
lua_pop(L, 1);
}
}
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <script.lua>\n";
return 1;
}
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if (luaL_dofile(L, argv[1]) != 0) {
std::cerr << "lua_dofile failed: " << lua_tostring(L, -1) << '\n';
lua_close(L);
return 1;
}
lua_getglobal(L, "perform");
std::vector<float> iv(2000, 1);
std::vector<float> ov(2000, 2);
as_table(L, iv.begin(), iv.end());
as_table(L, ov.begin(), ov.end());
if (lua_pcall(L, 2, 1, 0) != 0) {
std::cerr << "lua_pcall failed: " << lua_tostring(L, -1)
<< '\n';
lua_close(L);
return 1;
}
std::vector<float> w(2000);
from_table(L, w.begin(), w.end());
assert(std::all_of(w.begin(), w.end(),
[](float p) { return p == 3.0f; }));
}
这是用于上述测试用例的 Lua 小脚本。
function perform(v1,v2)
local n = math.min(#v1,#v2)
local v = {}
for i = 1,n do
v[i] = v1[i] + v2[i]
end
return v
end
std::vector 作为用户数据
如果大多数数据操作是在 Lua 端完成的,那么将向量作为 table 推送是有利的,因为 Lua table 实际上非常快.然而,如果大部分计算是在 C++ 端完成的,而 Lua 端只会在 C++ 实现的函数之间传递数据,这种方法会增加大量开销,因为我们会花费大量时间转换在 Lua table 和 std::vector
之间来回。为此,Lua 提供了 userdata,一种包装 C/C++ 数据结构的方法,使它们看起来像本机 Lua 数据类型。缺点是,当提供检查来自 Lua 的用户数据的函数时,这些函数通常很慢,因为必须反复检查参数并且必须调用多个嵌套函数。将它与 metatables 结合起来,为数组访问和长度操作提供语法糖,你会发现自己陷入了性能地狱。
也就是说,我构建了一个将向量推送为用户数据并设置其元数据的示例table。此过程也在“Lua 编程”一书的 28.1 – Userdata 章(阅读!)中进行了描述。
#include <iostream>
#include <vector>
#include <lua.hpp>
std::vector<float>& checkvector(lua_State *L, int index) {
std::vector<float> *v = *static_cast<std::vector<float> **>(
luaL_checkudata(L, index, "std::vector<float>"));
luaL_argcheck(L, v != nullptr, index, "invalid pointer");
return *v;
}
static int newvector(lua_State *L) {
size_t size = luaL_checkinteger(L, 1);
luaL_argcheck(L, size >= 0, 1, "invalid size");
*static_cast<std::vector<float> **>(lua_newuserdata(
L, sizeof(std::vector<float> *))) = new std::vector<float>(size);
luaL_getmetatable(L, "std::vector<float>");
lua_setmetatable(L, -2);
return 1;
}
void pushvector(lua_State *L, std::vector<float> const &v) {
std::vector<float> *udata = new std::vector<float>();
*udata = v;
*static_cast<std::vector<float> **>(lua_newuserdata(
L, sizeof(std::vector<float> *))) = udata;
luaL_getmetatable(L, "std::vector<float>");
lua_setmetatable(L, -2);
}
static int deletevector(lua_State *L) {
delete &checkvector(L, 1);
return 0;
}
static int setvector(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
size_t index = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, index < v.size(), 2, "index out of range");
luaL_argcheck(L, lua_isnumber(L, 3), 3, "not a number");
float record = lua_tonumber(L, 3);
v.at(index) = record;
return 0;
}
static int getvector(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
size_t index = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, index < v.size(), 2, "index out of range");
lua_pushnumber(L, v.at(index));
return 1;
}
static int getsize(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
lua_pushinteger(L, v.size());
return 1;
}
static int vectortostring(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
lua_pushfstring(L, "std::vector<float>(%d)", v.size());
return 1;
}
static const struct luaL_Reg vector_float_lib[] = {
{"new", newvector},
{nullptr, nullptr} // sentinel
};
static const struct luaL_Reg vector_float_meta[] = {
{"__tostring", vectortostring},
{"__newindex", setvector},
{"__index", getvector},
{"__len", getsize},
{"__gc", deletevector},
{nullptr, nullptr} // sentinel
};
int luaopen_vector_float(lua_State *L) {
luaL_newmetatable(L, "std::vector<float>");
luaL_setfuncs(L, vector_float_meta, 0);
luaL_newlib(L, vector_float_lib);
return 1;
}
static int send_vector(lua_State *L) {
std::vector<float> v = { 1, 2, 3, 4 };
pushvector(L,v);
return 1;
}
static int retrieve_vector(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
for (auto const &p : v) {
std::cout << p << '\n';
}
return 0;
}
int main(int argc, char *argv[]) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaL_requiref(L, "vector", luaopen_vector_float, 1);
lua_pop(L, 1);
lua_pushcfunction(L,send_vector);
lua_setglobal(L,"send_vector");
lua_pushcfunction(L,retrieve_vector);
lua_setglobal(L,"retrieve_vector");
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <script.lua>\n";
return 1;
}
luaL_dofile(L, argv[1]);
lua_close(L);
}
这样可以执行下面的Lua脚本
local v = send_vector()
for i = 1,#v do
v[i] = 2*v[i]
end
retrieve_vector(v)
给定一个全局函数 transform_vector
,例如
function transform_vector(v)
for i = 1,#v do
v[i] = 2*v[i]
end
return v
end
可以像使用任何其他 Lua 函数一样使用矢量参数调用此函数并检索矢量结果。
std::vector<float> v = { 1, 2, 3, 4 };
lua_getglobal(L,"transform_vector");
pushvector(L,v);
if (lua_pcall(L,1,1,0) != 0) {
// handle error
}
std::vector<float> w = checkvector(L, -1);
我正在尝试将浮点矢量从 C++ 函数传递给 Lua 函数作为 table 参数,然后在 Lua 函数调用后获取矢量的更新值.
这是简单的示例代码。
void myFunc(lua_State *L, std::vector<float> &vec) {
lua_getglobal(L, "myFunc");
lua_newtable(L);
for (size_t i=0; i<vec.size(); ++i) {
lua_pushinteger(L, i+1);
lua_pushnumber(L, vec[i]);
lua_settable(L, -3);
}
if (lua_pcall(L, 1, 0, 0) != 0) {
std::cout << "Error : Failed to call myFunc" << std::endl;
}
}
然后我可以调用这个函数如下。
std::vector<float> vec = {1,2,3,4,5}; //an array that will be sent to Lua as table
myFunc(L, vec); //call "myFunc" function in Lua and pass the array as an argument
/* <Lua function which will be called>
function myFunc(t)
for i=1, #t do
t[i] = t[i] * 2
end
end
*/
//how to update elements of "vec" here so it now becomes {2,4,6,8,10}?
正如我在代码中评论的那样,我想在调用 Lua 函数后更新 vector<float> vec
的元素。
是否可以将数组传递给Lua函数作为参考? (就像它在 C++ 函数中的工作方式一样)
如果不是,是否可以获取 Lua table(t) 的值,以便我可以在调用函数后将它们写回 C++ 中的浮点向量?
谢谢!
将 std::vector 转换为 Lua table
正如所讨论的那样 in chat 可能需要将函数参数从 std::vector<float>
转换为 Lua table 并将 return 值从 Lua table 到 std::vector<float>
。这样做的好处是Lua端完全透明
函数as_table
从一对迭代器创建一个新的table,from_table
从栈顶的Luatable转换为一对迭代器。
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
#include <lua.hpp>
template <typename T, typename U>
void as_table(lua_State* L, T begin, U end) {
lua_newtable(L);
for (size_t i = 0; begin != end; ++begin, ++i) {
lua_pushinteger(L, i + 1);
lua_pushnumber(L, *begin);
lua_settable(L, -3);
}
}
template <typename T, typename U>
void from_table(lua_State* L, T begin, U end) {
assert(lua_istable(L,-1));
for (size_t i = 0; begin != end; ++begin, ++i) {
lua_pushinteger(L, i + 1);
lua_gettable(L, -2);
*begin = lua_tonumber(L, -1);
lua_pop(L, 1);
}
}
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <script.lua>\n";
return 1;
}
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if (luaL_dofile(L, argv[1]) != 0) {
std::cerr << "lua_dofile failed: " << lua_tostring(L, -1) << '\n';
lua_close(L);
return 1;
}
lua_getglobal(L, "perform");
std::vector<float> iv(2000, 1);
std::vector<float> ov(2000, 2);
as_table(L, iv.begin(), iv.end());
as_table(L, ov.begin(), ov.end());
if (lua_pcall(L, 2, 1, 0) != 0) {
std::cerr << "lua_pcall failed: " << lua_tostring(L, -1)
<< '\n';
lua_close(L);
return 1;
}
std::vector<float> w(2000);
from_table(L, w.begin(), w.end());
assert(std::all_of(w.begin(), w.end(),
[](float p) { return p == 3.0f; }));
}
这是用于上述测试用例的 Lua 小脚本。
function perform(v1,v2)
local n = math.min(#v1,#v2)
local v = {}
for i = 1,n do
v[i] = v1[i] + v2[i]
end
return v
end
std::vector 作为用户数据
如果大多数数据操作是在 Lua 端完成的,那么将向量作为 table 推送是有利的,因为 Lua table 实际上非常快.然而,如果大部分计算是在 C++ 端完成的,而 Lua 端只会在 C++ 实现的函数之间传递数据,这种方法会增加大量开销,因为我们会花费大量时间转换在 Lua table 和 std::vector
之间来回。为此,Lua 提供了 userdata,一种包装 C/C++ 数据结构的方法,使它们看起来像本机 Lua 数据类型。缺点是,当提供检查来自 Lua 的用户数据的函数时,这些函数通常很慢,因为必须反复检查参数并且必须调用多个嵌套函数。将它与 metatables 结合起来,为数组访问和长度操作提供语法糖,你会发现自己陷入了性能地狱。
也就是说,我构建了一个将向量推送为用户数据并设置其元数据的示例table。此过程也在“Lua 编程”一书的 28.1 – Userdata 章(阅读!)中进行了描述。
#include <iostream>
#include <vector>
#include <lua.hpp>
std::vector<float>& checkvector(lua_State *L, int index) {
std::vector<float> *v = *static_cast<std::vector<float> **>(
luaL_checkudata(L, index, "std::vector<float>"));
luaL_argcheck(L, v != nullptr, index, "invalid pointer");
return *v;
}
static int newvector(lua_State *L) {
size_t size = luaL_checkinteger(L, 1);
luaL_argcheck(L, size >= 0, 1, "invalid size");
*static_cast<std::vector<float> **>(lua_newuserdata(
L, sizeof(std::vector<float> *))) = new std::vector<float>(size);
luaL_getmetatable(L, "std::vector<float>");
lua_setmetatable(L, -2);
return 1;
}
void pushvector(lua_State *L, std::vector<float> const &v) {
std::vector<float> *udata = new std::vector<float>();
*udata = v;
*static_cast<std::vector<float> **>(lua_newuserdata(
L, sizeof(std::vector<float> *))) = udata;
luaL_getmetatable(L, "std::vector<float>");
lua_setmetatable(L, -2);
}
static int deletevector(lua_State *L) {
delete &checkvector(L, 1);
return 0;
}
static int setvector(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
size_t index = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, index < v.size(), 2, "index out of range");
luaL_argcheck(L, lua_isnumber(L, 3), 3, "not a number");
float record = lua_tonumber(L, 3);
v.at(index) = record;
return 0;
}
static int getvector(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
size_t index = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, index < v.size(), 2, "index out of range");
lua_pushnumber(L, v.at(index));
return 1;
}
static int getsize(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
lua_pushinteger(L, v.size());
return 1;
}
static int vectortostring(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
lua_pushfstring(L, "std::vector<float>(%d)", v.size());
return 1;
}
static const struct luaL_Reg vector_float_lib[] = {
{"new", newvector},
{nullptr, nullptr} // sentinel
};
static const struct luaL_Reg vector_float_meta[] = {
{"__tostring", vectortostring},
{"__newindex", setvector},
{"__index", getvector},
{"__len", getsize},
{"__gc", deletevector},
{nullptr, nullptr} // sentinel
};
int luaopen_vector_float(lua_State *L) {
luaL_newmetatable(L, "std::vector<float>");
luaL_setfuncs(L, vector_float_meta, 0);
luaL_newlib(L, vector_float_lib);
return 1;
}
static int send_vector(lua_State *L) {
std::vector<float> v = { 1, 2, 3, 4 };
pushvector(L,v);
return 1;
}
static int retrieve_vector(lua_State *L) {
std::vector<float> &v = checkvector(L, 1);
for (auto const &p : v) {
std::cout << p << '\n';
}
return 0;
}
int main(int argc, char *argv[]) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaL_requiref(L, "vector", luaopen_vector_float, 1);
lua_pop(L, 1);
lua_pushcfunction(L,send_vector);
lua_setglobal(L,"send_vector");
lua_pushcfunction(L,retrieve_vector);
lua_setglobal(L,"retrieve_vector");
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <script.lua>\n";
return 1;
}
luaL_dofile(L, argv[1]);
lua_close(L);
}
这样可以执行下面的Lua脚本
local v = send_vector()
for i = 1,#v do
v[i] = 2*v[i]
end
retrieve_vector(v)
给定一个全局函数 transform_vector
,例如
function transform_vector(v)
for i = 1,#v do
v[i] = 2*v[i]
end
return v
end
可以像使用任何其他 Lua 函数一样使用矢量参数调用此函数并检索矢量结果。
std::vector<float> v = { 1, 2, 3, 4 };
lua_getglobal(L,"transform_vector");
pushvector(L,v);
if (lua_pcall(L,1,1,0) != 0) {
// handle error
}
std::vector<float> w = checkvector(L, -1);