[英]How to have a Lua iterator return a C struct?
I have a Visual Studio 2008 C++03 project using Lua 5.2.1 where I would like to have an iterator return an object such that I can get parameter values or call related functions. 我有一个使用Lua 5.2.1的Visual Studio 2008 C ++ 03项目,我希望迭代器返回一个对象,以便我可以获取参数值或调用相关函数。 For example:
例如:
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
I am using the following C++ code to implement this (error checking omitted): 我使用以下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;
}
But, when I run this, I get the Lua error: foo.lua:2: calling 'bar' on bad self
但是,当我运行这个时,我得到了Lua错误:
foo.lua:2: calling 'bar' on bad self
I realize there are wrappers that may do this, but I would prefer to understand the underlying Lua mechanism before implementing any wrappers. 我意识到有些包装器可以做到这一点,但我更愿意在实现任何包装器之前理解底层的Lua机制。
You're returning a metatable from your iterator, not a foo
instance. 你从迭代器返回metatable,而不是
foo
实例。
More importantly, your metatable contains methods, but no metamethods. 更重要的是,你的metatable包含方法,但没有metamethods。 In particular, if you want
foo
method calls to resolve to the methods in your metatable, you'll need to set the __index
metamethod. 特别是,如果您希望
foo
方法调用解析为metatable中的方法,则需要设置__index
元方法。
I'd recommend learning how metatables work in Lua, very well, before implementing the same via the C API. 我建议在通过C API实现相同功能之前,先了解meta在Lua中的工作原理。
When you say foo.id
, if id
doesn't exist in foo
(or foo
is a userdata) and foo
has a metatable with __index
set, this will resolve to one of two things: 当你说
foo.id
,如果foo
中不存在id
(或者foo
是用户数据)并且foo
具有设置了__index
的metatable,这将解析为以下两种情况之一:
__index
is a function, that function will be called with a string id
and foo.id
resolves to whatever that function returns. __index
是一个函数,那么将使用字符串id
调用该函数,并且foo.id
将解析为该函数返回的任何内容。 __index
is a table, the value stored in rawget(__index, 'id')
so foo.id
essentially resolve to `rawget(getmetatable(foo).__index, 'id'). __index
是一个表,那么存储在rawget(__index, 'id')
使得foo.id
基本上解析为`rawget(getmetatable(foo).__ index,'id')。 So if you want to use foo:id()
, you can create a generic id
method foo's metatable that returns the value self.id
. 因此,如果你想使用
foo:id()
,你可以创建一个通用的id
方法foo的metatable,它返回值self.id
If you want to use foo.id
, you either need to change foo
to a table and store id
as part of it's state, or implement __index
as a function where you do string compares and recognize that id
should resolve to self.id
. 如果你想使用
foo.id
,你需要将foo
更改为一个表并将id
作为其状态的一部分进行存储,或者将__index
实现为一个函数,在这里你进行字符串比较并识别id
应解析为self.id
Here's a modified, simplified version of your code which shows the __index
metamethod working: 这是代码的修改后的简化版本,它显示了
__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;
}
A test: 一个测试:
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
Output: 输出:
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.