Преобразование std :: vector в таблицу Lua и из нее
Как уже говорилось в чате , может быть желательно преобразовать аргументы функции из std::vector<float>
в таблицы Lua и возвращаемое значение изЛуа стол до std::vector<float>
.Преимущество этого состоит в том, что он полностью прозрачен в конце Lua.
Функция as_table
создает новую таблицу из пары итераторов, from_table
преобразует таблицу Lua поверх стека впара итераторов.
#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 as userdata
* 1018Выдвижение вектора в виде таблицы полезно, если большая часть манипуляций с данными выполняется на конце Lua, потому что таблицы Lua на самом деле довольно быстрые.Однако, если бы большинство вычислений выполнялось на стороне C ++, а конец Lua передавал бы только данные между функциями, реализованными в C ++, такой подход привел бы к большим накладным расходам, потому что мы тратили бы много времени на преобразование назад и вперед междуLua и
std::vector
.Для этого Lua предоставляет
userdata , метод для обертывания структур данных C / C ++ таким образом, что они воспринимаются как нативные типы данных Lua.Недостатком является то, что при предложении функций для проверки пользовательских данных из Lua они обычно медленны, потому что аргументы должны проверяться многократно и вызываться несколько вложенных функций.Объедините это с метатаблями, чтобы иметь синтаксический сахар для доступа к массиву и операций с длиной, и вы окажетесь в адской производительности.
Тем не менее, я построил пример использования вектора как пользовательских данных и установил его метатабельность.Этот процесс также описан в главе 28.1 - Данные пользователя в книге «Программирование на Lua» (читай!).
#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);