英文:
Read lua object (metatable?) as std::tuple
问题
我有一些函数可以从 Lua(5.4)堆栈中读取元组,假设堆栈包含一个 Lua 表:
template <typename type_t>
type_t stack_value_as(lua_State *const L, int index)
{
// 在这里进行模板魔法...
return {};
}
template <typename type_t>
type_t table_value_at(lua_State *const L, int table_stack_position, int index)
{
lua_rawgeti(L, table_stack_position, index);
auto result = stack_value_as<type_t>(L, -1);
lua_pop(L, 1);
return result;
}
template <typename ... type_pack>
auto read_tuple(lua_State *L, int index)
{
using tuple_t = std::tuple<type_pack ...>;
if (lua_istable(L, index))
{
const auto size_lua = lua_rawlen(L, index);
constexpr auto size_cpp = sizeof...(type_pack);
if (size_lua == size_cpp)
{
auto helper = [&](const std::integer_sequence<int, index_pack ...> &)
{
return tuple_t{ table_value_at<type_pack>(L, index, index_pack - index) ... };
};
return helper(std::make_integer_sequence<int, size_cpp>{});
}
}
return tuple_t{};
}
当我使用下面的 Lua 脚本测试这段代码时,它失败了:
test = {e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."};
-- 调用 1:成功
tup({1, 2.3456, "789"})
-- 调用 2 和 3:lua_istable 返回 true,lua_rawlen 返回 0
tup(test)
tup({e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."})
函数 tup
绑定到一个接收 tuple<int, double, string>
参数的 C++ 函数。当堆栈包含一个 Lua 对象时,lua_istable
函数返回真,但 lua_rawlen
返回 0
。我猜测在这种情况下,内容是一个 Lua 元表。我该如何将这些 Lua 对象(元表?)读取为 std::tuple
?
英文:
I have some functions that can read tuples from Lua (5.4) stack, assuming that the stack contains a Lua table:
template <typename type_t>
type_t stack_value_as(lua_State *const L, int index)
{
// template magic happens here…
return {};
}
template <typename type_t>
type_t table_value_at(lua_State *const L, int table_stack_position, int index)
{
lua_rawgeti(L, table_stack_position, index);
auto result = stack_value_as<type_t>(L, -1);
lua_pop(L, 1);
return result;
}
template <typename ... type_pack>
auto read_tuple(lua_State *L, int index)
{
using tuple_t = std::tuple<type_pack ...>;
if (lua_istable(L, index))
{
const auto size_lua = lua_rawlen(L, index);
constexpr auto size_cpp = sizeof...(type_pack);
if (size_lua == size_cpp)
{
auto helper = [&]<int ... index_pack>(const std::integer_sequence<int, index_pack ...> &)
{
return tuple_t{ table_value_at<type_pack>(L, index, index_pack - index) ... };
};
return helper(std::make_integer_sequence<int, size_cpp>{});
}
}
return tuple_t{};
}
When I test this code with the Lua script below it fails:
test = {e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."};
-- call 1: ok
tup({1, 2.3456, "789"})
-- call 2 & 3: lua_istable is true, lua_rawlen is 0
tup(test)
tup({e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."})
The function tup
is tied to a C++ function that receives tuple<int, double, string>
as parameter. When the stack contains a Lua object the lua_istable
function returns truthy but the lua_rawlen
returns 0
. My guess is that in this case the content is a LUA metatable. How can I read those Lua objects (metatables?) as std::tuple
?
答案1
得分: 1
关于 lua_rawlen == 0
在这段代码中,根据Lua 5.4参考手册,lua_rawlen == 0
是正确的。
根据lua_rawlen的说明:
> 返回给定索引处值的原始“长度”:[...];对于表,这是没有元方法的长度运算符('#')的结果;
根据长度运算符的说明:
> 应用于表的长度运算符返回表中的边界。表t中的边界是满足以下条件的任何非负整数:
>
> (border == 0 or t[border] ~= nil) and
> (t[border + 1] == nil or border == math.maxinteger)
>
> 简单来说,边界是表中存在的正整数索引,其后紧跟一个不存在的索引,还有两个特殊情况:当索引1不存在时,边界为0;当索引为整数的最大值时,边界为最大整数值。请注意,非正整数的键不会影响边界。
在我们的例子中,对于任何整数i
,test[i] == nil
,因此0是该表的唯一边界。结论:#test == 0
。
简而言之,一般情况下,长度运算符不会计算Lua表的键值对数量。它在{1, 2.3456, "789"}
这种情况下可以计算,因为它是一个数组:所有的键都是从1开始的连续整数。在其他情况下,长度运算符没有太多实际价值。
tup({1, 2.3456, "789"}) vs tup(test)
根据test
的声明方式,所有的键值对都具有字符串键。一个等价的声明方式是:
# test = {e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."}
test = {}
test['e'] = 42
test['n'] = 3.1415
test['id'] = "test, test, 1, 2, 3..."
这与具有整数键的表不同:
# {1, 2.3456, "789"}
equivalent = {}
equivalent[1] = 1
equivalent[2] = 2.3456
equivalent[3] = "789"
在所有情况下获取正确的键值对数量是一个简单的修复。然而,对于元组转换来说存在一个更大的问题。从根本上讲,Lua表类似于C++的std::unordered_map
。在{e = 42, n = 3.14, id = "test"}
中,键值对没有固定的顺序:("e",42)
不是第一个键值对,("n",3.14)
也不是第二个。如果在C++函数中迭代Lua表的所有键值对,可能会以不同的顺序获取它们。你需要对字符串键进行一些逻辑处理,以获得元组元素的一致顺序。
英文:
About lua_rawlen == 0
test = {e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."};
-- call 2 & 3: lua_istable is true, lua_rawlen is 0
tup(test)
In this snippet, lua_rawlen == 0
is correct according to Lua 5.4 reference manual.
From lua_rawlen:
> Returns the raw "length" of the value at the given index: [...]; for tables, this is the result of the length operator ('#') with no metamethods;
From the length operator:
> The length operator applied on a table returns a border in that table. A border in a table t is any non-negative integer that satisfies the following condition:
>
> (border == 0 or t[border] ~= nil) and
> (t[border + 1] == nil or border == math.maxinteger)
>
>In words, a border is any positive integer index present in the table that is followed by an absent index, plus two limit cases: zero, when index 1 is absent; and the maximum value for an integer, when that index is present. Note that keys that are not positive integers do not interfere with borders.
In our case, test[i] == nil
for any integer i
, so 0 is the only border of this table. Conclusion: #test == 0
.
The TL,DR: in general, the length operator doesn't compute the number of (key/value) pairs of a Lua table. It does so with {1, 2.3456, "789"}
because it is an array: all its keys are consecutive integers starting from 1. In any other case, the length operator doesn't have much practical value.
tup({1, 2.3456, "789"}) vs tup(test)
The way test
was declared, all (key/value) pairs have string keys. An equivalent declaration would be:
# test = {e = 42, n = 3.1415, id = "test, test, 1, 2, 3..."}
test = {}
test['e'] = 42
test['n'] = 3.1415
test['id'] = "test, test, 1, 2, 3..."
Which is different from a table with integer keys:
# {1, 2.3456, "789"}
equivalent = {}
equivalent[1] = 1
equivalent[2] = 2.3456
equivalent[3] = "789"
Getting the correct number of (key/value) pairs in all cases is an easy fix. However for the tuple conversion there is a bigger problem. Fundamentally, Lua tables are similar to c++'s std::unordered_map
. In {e = 42, n = 3.14, id = "test"}
the pairs have no inherent order: ("e",42)
isn't the 1st pair, nor is ("n",3.14)
the 2nd. If you iterate over all (key/value) pairs of a Lua table in your c++ function, you can get them in a different order. You'll have to do some logic with the string keys to get a consistent order on the tuple's elements.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论