3

Lua5.2.1を使用するVisualStudio2008 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メカニズムを理解したいと思います。

4

1 に答える 1

2

インスタンスではなく、イテレータからメタテーブルを返しfooます。

さらに重要なことに、メタテーブルにはメソッドが含まれていますが、メタメソッドは含まれていません。特に、fooメソッド呼び出しをメタテーブル内のメソッドに解決する場合は、メタメソッドを設定する必要があります__index

C APIを介してメタテーブルを実装する前に、Luaでメタテーブルがどのように機能するかをよく学ぶことをお勧めします。

に存在せず(またはuserdataであり)、setを使用してメタテーブルがある場合foo.id、これは次の2つのいずれかに解決されます。idfoofoofoo__index

  1. が関数の場合__index、その関数は文字列で呼び出され、その関数が返すものに解決されますidfoo.id
  2. __indexがテーブルの場合、に格納されてrawget(__index, 'id')いる値はfoo.id基本的に `rawget(getmetatable(foo).__ index、'id')に解決されます。

したがって、を使用する場合は、値を返すジェネリックメソッドfooのメタテーブルをfoo:id()作成できます。idself.id

を使用する場合は、テーブルfoo.idに変更してその状態の一部として格納するか、文字列比較を実行して、に解決される必要があることを認識する関数として実装する必要があります。fooid__indexidself.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
于 2012-08-23T18:10:57.960 に答える