如何让Lua迭代器返回C结构
How to have a Lua iterator return a C struct?
我有一个使用Lua 5.2.1的Visual Studio 2008 C++03项目,我希望迭代器返回一个对象,这样我就可以获取参数值或调用相关函数。例如:
for f in foo.list() do
if f:bar() then
print("success")
else
print("failed")
end
print(string.format( "%d: %s", f.id, f.name))
end
我使用以下C++代码来实现这一点(省略了错误检查):
struct Foo {
int id;
char name[ 256 ];
HANDLE foo_handle;
}
int foo_list( lua_State* L )
{
Foo* f = ( Foo* )lua_newuserdata( L, sizeof( Foo ) );
ZeroMemory( f, sizeof( Foo ) );
luaL_getmetatable( L, foo_metatable );
lua_setmetatable( L, -2 );
f->foo_handle = CreateFooHandle();
lua_pushcclosure( L, foo_iter, 1 );
return 1;
}
int foo_iter( lua_State* L )
{
Foo* foo = ( Foo* )lua_touserdata( L, lua_upvalueindex( 1 ) );
if( GetNextFoo( foo ) ) /*sets the id and name parameters*/
{
// is this correct? I need to return some object...
luaL_getmetatable( L, foo_metatable );
return 1;
}
return 0;
}
int foo_name( lua_State* L )
{
Foo* f = ( Foo* )luaL_checkudata( L, 1, foo_metatable );
lua_pushstring( L, f->name );
return 1;
}
int foo_id( lua_State* L )
{
Foo* f = ( Foo* )luaL_checkudata( L, 1, foo_metatable );
lua_pushinteger( L, f->id );
return 1;
}
int foo_bar( lua_State* L )
{
Foo* f = ( Foo* )luaL_checkudata( L, 1, foo_metatable );
if( FooBar( f ) )
lua_pushboolean( L, true );
else
lua_pushboolean( L, false );
return 1;
}
int foo_close( lua_State* L ) { /*omitted. this part works*/ }
extern "C" int luaopen_foo( lua_State* L )
{
// how do I differentiate between a parameter get and a function call?
const luaL_Reg foo_methods[] = {
{ "name", foo_name },
{ "id", foo_id },
{ "bar", foo_bar },
{ "__gc", foo_close },
{ NULL, NULL }
};
luaL_newmetatable( L, foo_metatable );
luaL_setfuncs( L, foo_methods, 0 );
const luaL_Reg foo[] = {
{ "list", foo_list }
{ NULL, NULL }
};
luaL_newlib( L, foo );
return 1;
}
但是,当我运行这个时,我得到了Lua错误:foo.lua:2: calling 'bar' on bad self
我意识到有一些包装器可以做到这一点,但在实现任何包装器之前,我更希望了解底层的Lua机制。
您从迭代器返回的是元表,而不是foo
实例。
更重要的是,元表包含方法,但没有元方法。特别是,如果希望foo
方法调用解析为元表中的方法,则需要设置__index
元方法。
在通过C API实现元表之前,我建议学习元表在Lua中的工作原理。
当你说foo.id
时,如果foo
中不存在id
(或者foo
是用户数据),并且foo
有一个设置了__index
的元表,这将解决以下两个问题之一:
- 如果
__index
是一个函数,那么该函数将用字符串id
调用,并且foo.id
解析为该函数返回的任何值 - 如果
__index
是一个表,则存储在rawget(__index, 'id')
中的值,因此foo.id
本质上解析为`rawget(getmetatable(foo)__索引,'id')
因此,如果要使用foo:id()
,可以创建一个返回值self.id
的通用id
方法foo的元表。
如果要使用foo.id
,则需要将foo
更改为一个表并将id
作为其状态的一部分存储,或者将__index
实现为一个函数,在该函数中进行字符串比较并识别出id
应解析为self.id
。
以下是您代码的修改、简化版本,显示了__index
元方法的工作原理:
static int nextFooId = 0;
struct Foo {
int id;
char name[ 256 ];
};
static const char* foo_metatable = "foo";
int foo_iter( lua_State* L )
{
if (++nextFooId >= 10)
return 0;
// create and initialize foo
Foo* foo = ( Foo* )lua_newuserdata( L, sizeof( Foo ) );
foo->id = nextFooId;
sprintf(foo->name, "Foo %d", foo->id);
// set metatable for foo
luaL_getmetatable( L, foo_metatable );
lua_setmetatable( L, -2 );
return 1;
}
int foo_list( lua_State* L )
{
lua_pushcclosure( L, foo_iter, 1 );
return 1;
}
int foo_name( lua_State* L )
{
Foo* f = ( Foo* )luaL_checkudata( L, 1, foo_metatable );
lua_pushstring( L, f->name );
return 1;
}
int foo_id( lua_State* L )
{
Foo* f = ( Foo* )luaL_checkudata( L, 1, foo_metatable );
lua_pushinteger( L, f->id );
return 1;
}
int foo_bar( lua_State* L )
{
lua_pushboolean( L, rand()%2 );
return 1;
}
int foo_close( lua_State* L ) { return 0;/*omitted. this part works*/ }
extern "C" int luaopen_foo( lua_State* L )
{
const luaL_Reg foo_methods[] = {
{ "name", foo_name },
{ "id", foo_id },
{ "bar", foo_bar },
{ "__gc", foo_close },
{ NULL, NULL }
};
luaL_newmetatable( L, foo_metatable );
luaL_setfuncs( L, foo_methods, 0 );
// copy the metatable to the top of the stack
// and set it as the __index value in the metatable
lua_pushvalue(L, -1);
lua_setfield( L, -2, "__index");
const luaL_Reg foo[] = {
{ "list", foo_list },
{ NULL, NULL },
};
luaL_newlib( L, foo );
return 1;
}
测试:
foo = require 'foo'
for f in foo.list() do
if f:bar() then
print("success")
else
print("failed")
end
print(string.format( "%d: %s", f:id(), f:name()))
end
输出:
success
1: Foo 1
success
2: Foo 2
failed
3: Foo 3
failed
4: Foo 4
success
5: Foo 5
failed
6: Foo 6
failed
7: Foo 7
failed
8: Foo 8
failed
9: Foo 9
failed
10: Foo 10
相关文章:
- 在类中返回结构时出现问题
- 在 C 函数中返回结构会导致分段错误
- 如何从函数返回结构
- c++优化地返回结构和向量
- 从函数 BY VALUE 返回数组,返回结构时会发生什么?
- C++,使用 if/else 返回结构中的值
- 如何在C++中返回结构?
- 难以从另一个结构中的函数返回结构
- 返回结构的方法的正确语法是什么
- C++ 使用成员函数返回结构体指针属性的值
- 什么安全?从函数返回结构或指针
- 为什么要定义一个返回结构的 lambda 函数,而不是直接定义结构
- C 如何将数据文件读取到结构或向量以及返回结构或向量
- 从线程执行的函数中返回结构数组
- 取决于参数的数量,将其返回结构对象,并将其分配给常规结构的对象
- 返回结构数组 c++
- 如何从 C++ 中的函数返回结构
- 从类的方法返回结构中的值
- 如何从C 中的类返回结构
- 返回结构的特定值