4

私はLuaPlusと協力して、スクリプト言語でモジュールの機能を公開してきました。そのため、LuaPlusは本当に素晴らしいですが、c ++オブジェクトを表すluaオブジェクトの削除を処理する方法がわからないため、公開されたオブジェクトのクリーンアップに固執しているため、c++リソースを正しく解放できます。

私はluaテーブルとメタテーブルを使用してcppオブジェクトを表し、cppオブジェクトへのポインターをテーブルのlightuserdataパラメーター "__object"として渡すので、次のようなことができます。

function foo() 
  local p = MyCppObject:new()   //Create a cpp object and bind it to a table+metatable
  p:do_something()        //Correctly calls the cpp member function do_something
  ....
  (exits scope)           //No more references to p, so p is deleted.
 end

関数の終了後(またはしばらくして)、メタテーブルメソッド "__gc"の呼び出しを取得することを期待しました。ここで、内部cppオブジェクトのdeleteを呼び出しますが、cppコールバックがで呼び出されているのがわかりません。すべて。luaの関数collectgarbageを使用してガベージコレクションを強制しようとしました。これは、luaにオブジェクトを収集させるために関数を何度も呼び出しましたが、コールバックが実行されているのがわかりません。その上、collectgarbage( "count")を呼び出した結果が時々減少するので、どこかで何かが削除されているのがわかりますが、何がわかりません。luaのドキュメントを確認しましたが、何が間違っているのかわかりません:(

コメントは大歓迎です!ありがとう!


更新:C++コード側を追加+Mudが指摘したようにローカルを追加+私のテストのサンプル

私は自分のプログラムのこの小さなサンプルを作成しました。LuaShellオブジェクトは、状態のラッパー+コマンドラインを読み取り、std::cinから読み取った文字列を実行するループです。

#include <iostream>
#include "LuaPlus.h"
class Point
{
  private:
      int x_,y_;
  public:
  Point(): x_(0), y_(0){}
  Point(int a, int b): x_(a), y_(b){}

  ~Point() {std::cout << "Point "<< x_ << ","
                      << y_ << "being deleted" << std::endl;} 

  int x() const  { return x_;} 
  int y() const  { return y_;}     
};

LuaPlus::LuaObject metatable;

int new_point( LuaPlus::LuaState* state)
{
  LuaPlus::LuaStack args(state);
  //std::cout << "Creating point!!" << std::endl;
  float x = 0, y = 0;
  if ( args.Count() == 3)
  {
      if (args[2].IsNumber() && args[3].IsNumber())
      {
          x = args[2].GetFloat();
          y = args[3].GetFloat();
      }
  }

  Point* p = new Point(x,y);
  LuaPlus::LuaObject lua_obj = state->CreateTable();
  lua_obj.SetLightUserData("__object", p);
  lua_obj.SetMetaTable( metatable );

  return 1;
}

int my_gc_event( LuaPlus::LuaState* state) 
{
  std::cout << "Calling gc_event from lua" << std::endl;
  return 0;
}

int main()
{

  /* Creating the object that holds the Lua interpreter as well as 
   * the command line
   */ 
  LuaShell::Shell shell(true);
  LuaPlus::LuaObject globals = shell.get_state()->GetGlobals();

  metatable = globals.CreateTable("PointMetaTable");
  metatable.SetObject("__index", metatable);
  metatable.Register("new", new_point);
  metatable.Register("__gc",my_gc_event);
  metatable.RegisterObjectDirect("x", (Point*)0 ,&Point::x);
  metatable.RegisterObjectDirect("y", (Point*)0 ,&Point::y);
  globals.SetObject("Point", metatable);

  //Get into the read-command-line-until-quit loop.
  shell.run();
  return 0;
}

lua側では、これを実行してテストします。

? k,b = collectgarbage("count") print (k*1024) 
> 33761
? for it=1,1000000 do foo() end   
? k,b = collectgarbage("count") print (k*1024) 
> 75315
? collectgarbage()  
? k,b = collectgarbage("count") print (k*1024)
> 32363

ご覧のとおり、luaランタイムによると「いくらかの」ガベージコレクションがありますが、プロセスのトップレポートを見ると、メモリは上がるだけで、下がることはありません。また、ポイントデストラクタ(実際には呼び出していないため、予期されていません)または「my_gc_event」内からのメッセージ(収集ガベージ作業中のある時点で呼び出されたと思われるため、予期されていません)からのメッセージは表示されません。

再度、感謝します!

4

2 に答える 2

2

正解(泥の小道具)がコメントに埋もれていたことを考えると、最終的なコードを追加してここに答えたほうがいいと思いました。

マッドが言ったように、私の問題はオブジェクトを公開するためにテーブルを使用しようとしたことでした。これは機能しますが、lua 5.1ランタイム(luaplusで使用される)はテーブルのガベージコレクターイベント(__gc)を呼び出さないため、luaplusのBoxPointerヘルパー関数を使用する必要がありました。また、公開されたオブジェクトのタイプを追跡するためのマップを追加しました。これらはvoid *として渡され、後で適切なクリーンアップのために正しくキャストする必要があります。テンプレートのトリックを使用して、クリーンアップするための非常に単純なシステムで終了しました。これがコードです(メイン関数は変更されておらず、Pointクラスの定義も変更されていません)。

#include <iostream>
#include <map>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include "LuaPlus.h"
#include "Shell.h"


//Function used to delete an object created and exposes to lua
template<typename type_to_delete>
void delete_wrapper(void* ptr)
{
    type_to_delete* typed_ptr = static_cast<type_to_delete*>(ptr);
    delete typed_ptr;
}

//My object metatable
LuaPlus::LuaObject metatable;
//Table to save function pointers for the correct deletion of exposed objects
std::map<void*,boost::function<void(void*)> > g_object_type_deleter;

int new_point( LuaPlus::LuaState* state)
{
    LuaPlus::LuaStack args(state);
    float x = 0, y = 0;
    if ( args.Count() == 3)
    {
        if (args[2].IsNumber() && args[3].IsNumber())
        {
            x = args[2].GetFloat();
            y = args[3].GetFloat();        
        }
    }

    Point* p = new Point(x,y);
    //This used to be a table, but given the shortcoming with the gc, now using this version
    LuaPlus::LuaObject lua_obj = state->BoxPointer(p);
    lua_obj.SetMetaTable( metatable );

    //Save the callback to delete the object we just created.
    g_object_type_deleter[static_cast<void*> (p)] = boost::bind( delete_wrapper<Point> , _1);
    return 1;
}

int my_gc_event( LuaPlus::LuaState* state) 
{
    //get the pointer lua is trying to delete.
    void *p = state->UnBoxPointer(1);

    //look for the delete callback with the type information
    std::map<void*,boost::function<void(void*)> >::iterator it = g_object_type_deleter.find(p);
    if (it == g_object_type_deleter.end() )
    {
        std::cout << "receiving callback to destroy object that was not indexed !" << std::endl;
        return 0;
    }
    //Actually call the delete function passing the pointer as parameter
    it->second(it->first);
    return 0;
}

みんなありがとう!

于 2012-08-20T15:55:42.323 に答える
1
function foo() 
  p = MyCppObject:new()   //Create a cpp object and bind it to a table+metatable
  p:do_something()        //Correctly calls the cpp member function do_something
  ....
  (exits scope)           //No more references to p, so p is deleted.
end

pはグローバルであるため、戻るときにスコープを離れません。その関数に字句スコープを設定する場合fooは、キーワードを使用する必要があります。local

lightuserdataパラメーター[...]メタテーブルメソッド「__gc」の呼び出しを取得することを期待していました

Lightuserdataはガベージコレクションされません。それはあなたの問題とは関係がないかもしれませんが(LuaBindがどのように機能するかはわかりません)、言及する価値があると思いました。


コメントへの回答:

__gc私は次のテストを作成しました。これは、メタテーブルにのみを含む1バイトの何もしないuserdataタイプを作成します。

#include "lauxlib.h"

static int foo_gc (lua_State* L) { 
   puts("__gc called");
   return 0; 
}

static int foo_new (lua_State* L) { 
   lua_newuserdata(L, 1);
   luaL_getmetatable(L, "foo");
   lua_setmetatable(L, -2);
   return 1;
}

int __declspec(dllexport) __cdecl luaopen_luagc (lua_State* L) {
   // create foo type
   static const struct luaL_reg foo[] = {
      { "__gc",        foo_gc       },
      NULL, NULL
   };
   luaL_newmetatable(L, "foo");
   luaL_openlib(L, NULL, foo, 0);

   // create constructor
   lua_register(L, "foo", foo_new);
   return 0;
}

次に、次のテストを実行すると、次のようになります。

require 'luagc'

for i=1,5 do
   foo()
end

collectgarbage("collect")

出力は次のとおりです。

__gc called
__gc called
__gc called
__gc called
__gc called
于 2012-08-10T16:31:32.450 に答える