[英]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機制。
你從迭代器返回metatable,而不是foo
實例。
更重要的是,你的metatable包含方法,但沒有metamethods。 特別是,如果您希望foo
方法調用解析為metatable中的方法,則需要設置__index
元方法。
我建議在通過C API實現相同功能之前,先了解meta在Lua中的工作原理。
當你說foo.id
,如果foo
中不存在id
(或者foo
是用戶數據)並且foo
具有設置了__index
的metatable,這將解析為以下兩種情況之一:
__index
是一個函數,那么將使用字符串id
調用該函數,並且foo.id
將解析為該函數返回的任何內容。 __index
是一個表,那么存儲在rawget(__index, 'id')
使得foo.id
基本上解析為`rawget(getmetatable(foo).__ index,'id')。 因此,如果你想使用foo:id()
,你可以創建一個通用的id
方法foo的metatable,它返回值self.id
如果你想使用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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.