首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何获取C++发送给Lua函数的表的更新值?

如何获取C++发送给Lua函数的表的更新值?
EN

Stack Overflow用户
提问于 2018-05-30 00:01:46
回答 1查看 1.8K关注 0票数 3

我尝试将一个浮点向量作为表参数从C++函数传递给Lua函数,然后在Lua函数调用后获得向量的更新值。

下面是简单的示例代码。

代码语言:javascript
复制
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;
    }
}

然后我可以像下面这样调用这个函数。

代码语言:javascript
复制
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++中的浮点向量?

谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-30 16:42:51

在std::vector与Lua表之间来回转换

正如in chat所讨论的,可能需要将函数参数从std::vector<float>转换为Lua表,并将返回值从Lua表转换为std::vector<float>。这样做的好处是它在Lua端是完全透明的。

函数as_table从一对迭代器创建一个新表,from_table将堆栈顶部的Lua表转换为一对迭代器。

代码语言:javascript
复制
#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脚本。

代码语言:javascript
复制
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端完成的,那么将向量作为表进行推送是有利的,因为Lua表实际上非常快。然而,如果大多数计算是在Lua端完成的,而Lua端只会在C++中实现的函数之间传递数据,这种方法将增加大量的开销,因为我们将花费大量时间在C++表和std::vector之间来回转换。为此,Lua提供了userdata,这是一种包装C/C++数据结构的方法,使它们表现为原生Lua数据类型。缺点是,当提供从Lua检查用户数据的函数时,这些函数通常很慢,因为参数必须重复检查,并且必须调用多个嵌套函数。将它与metatables结合起来,为数组访问和长度操作提供语法糖,你会发现自己陷入了性能地狱。

也就是说,我构建了一个将向量作为用户数据推送的示例,并设置了它的元表。这个过程在“在Lua中编程”一书的28.1 – Userdata一章中也有描述(请阅读它!)

代码语言:javascript
复制
#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脚本

代码语言:javascript
复制
local v = send_vector()
for i = 1,#v do
    v[i] = 2*v[i]
end
retrieve_vector(v)

给定一个全局函数transform_vector,例如

代码语言:javascript
复制
function transform_vector(v)
    for i = 1,#v do
        v[i] = 2*v[i]
    end
    return v
end

您可以使用向量参数调用此函数,并像使用任何其他Lua函数一样检索向量结果。

代码语言:javascript
复制
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);
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50588549

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档