gpt4 book ai didi

c++ - 如何让 Lua 迭代器返回 C 结构?

转载 作者:太空狗 更新时间:2023-10-29 21:47:54 24 4
gpt4 key购买 nike

我有一个使用 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 时,如果 id 不存在于 foo 中(或者 foo 是a userdata) 和 foo 有一个设置了 __index 的元表,这将解析为以下两种情况之一:

  1. 如果 __index 是一个函数,该函数将使用字符串 id 调用,并且 foo.id 解析为该函数返回的任何内容。
  2. 如果 __index 是一个表,则存储在 rawget(__index, 'id') 中的值因此 foo.id 基本上解析为 ` rawget(getmetatable(foo).__index, 'id').

所以如果你想使用foo:id(),你可以创建一个通用的id方法foo的元表返回值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

关于c++ - 如何让 Lua 迭代器返回 C 结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12096281/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com