2009-06-26 9 views
2

私はBitfighterのリードデベロッパで、主にC++で書かれたゲームですが、Luaを使ってロボットプレイヤーをスクリプトします。私たちはLunar(ルナの一種)を使ってビットを接着しています。ルアで古くなったC++リファレンスを検出する

私は現在、LuaのスクリプトがC++コードで参照されているオブジェクトが削除されていることを知ることができます。ここ

は(Luaの)いくつかのサンプルロボットのコードである:needTargetが真である場合に船にのみ設定されていることを

if needTarget then       -- needTarget => global(?) boolean 
    ship = findClosest(findItems(ShipType)) -- ship => global lightUserData obj 
end 

if ship ~= nil then 
    bot:setAngleToPoint(ship:getLoc()) 
    bot:fire() 
end 

お知らせ、そうでなければ、前の反復からの値が使用されています。変数が最後に設定されてから船が殺された(そしてC++によってそのオブジェクトが削除された)ことはかなり可能性があります(おそらく、ボットが仕事をしているということもあります:-)。もしそうなら、ship:getLoc()を呼び出すときにC++が適切に機能し、通常はクラッシュします。

したがって、プログラマが間違いを犯した場合に、どのようにして状況を最もうまく処理し、損害を制限するかが問題です。

私にはいくつかのアイデアがあります。

function itemDied(deaditem) 
    if deaditem == ship then 
     ship = nil 
     needTarget = true 
    end 
end 

第二には、私たちが「魔法」の問題を解決するために、スマートポインタを参照カウントのいくつかの並べ替えを実装することができます:船やその他の項目が死ぬとまず、C++のコードを呼び出すことができますLuaの機能のいくつかの並べ替えを作成することができます。しかし、私はこれでどこから始めるべきか分かりません。

if !isAlive(ship) then 
    needTarget = true 
    ship = nil    -- superfluous, but here for clarity in this example 
end 

if needTarget then       -- needTarget => global(?) boolean 
    ship = findClosest(findItems(ShipType)) -- ship => global lightUserData obj 
end 

<...as before...> 

第四に、私は、船の唯一のIDを保持でき、ではなく:

第三に、我々はいくつかのdeadness検出器の一種(それがうまくいくかどうかはわかりません)ボットはそうのように呼び出すことができることを持つことができますこのような船オブジェクトに各サイクルを取得するための基準、および使用:

local ship = getShip(shipID)     -- shipID => global ID 

if ship == nil then 
    needTarget = true 
end 

if needTarget then       -- needTarget => global(?) boolean 
    ship = findClosest(findItems(ShipType)) -- ship => global lightUserData obj 
    shipID = ship:getID() 
end 

<...as before...> 

私の理想的な状況にもインテリジェントにエラーをスローしていました。私が死んだ船でgetLoc()メソッドを実行した場合は、ボットに復旧の機会を与えるか、少なくともシステムをロボットを殺して問題を記録するようにエラー処理コードをトリガーしたいと思います。私のボットをどのようにコード化するかにもっと注意する必要があります。

これは私の考えです。私は#1に傾いていますが、厄介な気がします(弾丸のような短いライフサイクルのオブジェクトがたくさんあるので、多くのことが往復しているかもしれませんが、ほとんどは追跡しません)。 itemDied()関数の実装を忘れることは簡単かもしれません。 #2は魅力的ですが、私は魔法が好きですが、どのように動くか分かりません。 #3 &#4は非常に分かりやすく、いくつかのゲームサイクル(おそらく単一の船)の間に面白い少数のオブジェクトにしか私の死の検出を制限することはできません。

これは一般的な問題です。あなたはこれらのアイデアをどう思いますか、そこには優れたものがありますか?

ありがとうございます!


ここに私の現在の最善の解決策だ:C++で

、私の船のオブジェクトは、そのライフサイクルC++によって制御されている船、と呼ばれています。各Shipに対して、私はShuaへのポインタを含むLuaShipと呼ばれるプロキシオブジェクトを作成し、ShipにはLuaShipへのポインタが含まれています。Shipのデストラクタでは、LuaShipのShipポインタをNULLに設定しました。これは、船が破壊されたという指標として使用されます。

私のLuaコードはLuaShipへの参照しか持っていないので、理論的には少なくともこの部分は依然として適切に機能していません。Luaは対応するShipオブジェクトがなくなるとLuaShipのライフサイクルを制御します。したがって、Shuaオブジェクトがなくなっても、Luaは常に有効なハンドルを持ち、ShipがNULLであることをチェックするShipメソッドのプロキシメソッドを書くことができます。

これで私の仕事は、Luna/Lunarがポインタのライフサイクルをどのように管理しているかをよりよく理解することです。また、Luaコードを指し示しているLuaコードが残っていると、それは非常にやりやすいはずです。


実際、少なくとも私によってではないことが判明しました。何がうまくいくかは、ShipとLuaShipのオブジェクトを少し切り離すことでした。さて、LuaスクリプトがLuaShipオブジェクトをリクエストすると、新しいものを作成してLuaに渡し、Luaがそれを完了したら削除します。 LuaShipはスマートポインタを使用してShipを参照するため、Shipが終了すると、そのポインタはNULLに設定され、LuaShipオブジェクトが検出できます。

Luaコーダーは、使用する前に船がまだ有効であることを確認する必要があります。もしそうでなければ、(前に起こったように)ゲーム全体がクラッシュするのではなく、召喚を捕まえて、厳しいエラーメッセージを投げ捨てることができます。

LuaはLuaShipのライフサイクルを完全に制御でき、C++は問題を起こすことなく船を削除することができ、すべてがスムーズに動作するようです。唯一の欠点は、私が潜在的に多くのLuaShipオブジェクトを作成していることですが、それほど悪くはありません。


あなたは、このトピックに興味がある場合は、上記を精製するためにいくつかの提案で終わること、私は関連する概念について投稿メーリングリストのスレッドを参照してください。

http://lua-users.org/lists/lua-l/2009-07/msg00076.html

+0

あなたは[luabridge](https://github.com/vinniefalco/LuaBridge)や[luabind](http://www.rasterbar.com/products/luabind/docs.html)のようなもっと精巧なものを試してみましたか? )?どちらもメモリリークのない寿命が長い(lua/C++)オブジェクトを許可します –

答えて

5

私はあなたのルア側に問題があるとは思わないので、そこで解決するべきではありません。

あなたのC++コードは、まだ参照されているオブジェクトを削除しています。彼らがどのように参照されても、それは悪いです。

簡単な解決策は、すべてのオブジェクトをクリーンアップすることです。スクリプトはそれらを使用しているのでどのオブジェクトを生かしておかなければならないかを既に知っていますし、ランダムなC++オブジェクトに対してGCを実行させることも可能です(もちろんC++側のスマートポインタを想定すると、スマートポインタごとにLunars参照カウント)

+0

はい、あなたは正しいです。私は問題がLuna/Lunarがどうやってそれを結びつけているかにあると思う。私はまだ細部を理解しようとしていますが、参照の際に弱いテーブルを使用しているように見えます.GCがオブジェクトを削除するのを妨げないと思います。うまくいけば私はそれをちょっと微調整して、少なくとも選択されたオブジェクト(上記の私のソリューションで説明したLuaShipなど)については、そうしないように伝えることができます。 – Watusimoto

4

当社ソリューションナンバー4になって、それは私たちのためにうまくいきました。私はそれをお勧めします。しかし、完全性のために:

番号1は実線です。船舶のデストラクタがいくつかの月のコードを呼び出しさせるようにしてください(または、それが呼び出されるべきであることをマークしてください)。このようにすることは、ゲームエンジンとロボットを別々のスレッドで実行したい場合、ルアランタイムをちょっとハックしたり、信じられないほど慎重でなければならないことを意味します。

Number 2はあなたが思うほど難しくありません:C++側で参照カウントポインタを書いたり借りたりしてください。もしあなたのLua/C++グルーがC++ポインタを扱うのに慣れているのであれば、実行時などにシンボルテーブルを検査してバインディングを生成している場合を除きます。問題は、それがあなたのデザインにかなり深い変化を起こさせることです。参照カウントポインタを使用して船を参照している場合は、どこでも使用する必要があります。裸のポインタとスマートなものを混在させた船舶に固有のリスクが明らかになります。だから私はその道を行くことはありません、プロジェクトの遅れとしてではないようです。

3番はトリッキーです。あなたは、指定された船体がそれを表すメモリが解放された後であっても、生きているか死んでいるかを判断する方法が必要です。私が考えることができるすべての解決策は、基本的に4番になります。死んだ船は、Luaオブジェクトにコピーされた何らかの種類のトークンを残すことができ、死んだことを検出することができます:: setやそれに類するもの)、なぜ彼らのトークンで船を参照するだけではないのですか?

一般的に、特定のC++ポインタが削除されたオブジェクトを指しているかどうかを検出することはできません。したがって、問題を簡単に解決できる魔法の方法はありません。削除された船にship:getLoc()を呼び出すエラーをトラップすることは、デストラクタで特別な処置を取った場合にのみ可能です。この問題は完璧な解決策ではありません。幸いです。

+0

私の現在の解決策(私の質問の最後に掲載されています)は、あなたが提起したスレッドの懸念に対して実行されます。しかし、現時点では、私のスクリプトは古いラップトップでは60フレーム/秒近くで快適に動作し、スレッディングは複雑さが増すため、スレッディングは意味をなさないと思います。 私はShip <=> LuaShip接続にスマートポインタのタイプを使用していますので、ゲーム全体を改造することなくそのメリットを見ることができれば幸いです。 – Watusimoto

0

私はMSaltersに同意します。私は本当にあなたがC++側からメモリを解放するべきではないと思います。 Luaのユーザデータは、___gcメタメソッドをサポートしています。 gcが十分に積極的でない場合は、それをちょっと微調整したり、手動で小さなステップサイズで実行したりしてください。 lua gcは決定的なものではないので、リソースを解放する必要がある場合は、それらのリソースを解放するために呼び出すことができる関数を用意する必要があります(適切なチェックを付けて__gcによって呼び出されます)。

船のリファレンスにweak tablesを使用して、nilにすべての参照を割り当てて解放する必要がないようにすることもできます。強力な参照を1つ(例えば、すべてのアクティブな船のリストで)して、他のすべてが弱い参照であるとします。船が破壊されたら、その船にそれをマークする旗を掲げて、アクティブ船のテーブルにnilという参照を設定します。他の船は、あなたのロジックをやり取りしたい場合、あなたがチェックを除いてその後、同じである:

if ship==nil or ship.destroyed then 
    ship = findClosest(findItems(ShipType)) 
end 
2

これは古い質問ですが、適切なソリューションは、IMO、lua_newuserdata()を作成することですshared_ptrまたはweak_ptrboost::shared_ptr/boost::weak_ptrまたはC++11std::shared_ptr/std::weak_ptrのいずれかを使用します。そこから、必要に応じてリファレンスを作成するか、weak_ptrlock()shared_ptrを取得できない場合は失敗します。例えば、(新しいプロジェクトのために、私はお勧め可能な場合けれども、これは、あなたはおそらくまだC++11サポートしていていない古い問題であるので、この例ではブーストのshared_ptrを使用してC++ 11のshared_ptr):

using MyObjectPtr = boost::shared_ptr<MyObject>; 
using MyObjectWeakPtr = boost::weak_ptr<MyObject>; 

auto mySharedPtr = boost::make_shared<MyObject>(); 
auto userdata = static_cast<MyObjectWeakPtr*>(lua_newuserdata(L, sizeof(MyObjectWeakPtr))); 
new(userdata) MyObjectWeakPtr(mySharedPtr); 

そして、あなたはC++オブジェクトを取得する必要がある場合:

static int 
myobject_lua__gc(lua_State* L) { 
    auto weakObj = *static_cast<MyObjectWeakPtr*>(
     luaL_checkudata(L, 1, "MyObject.Metatable")); 
    luaL_argcheck(L, weakObj != nullptr, 1, "'MyObjectWeakPtr' expected"); 
    weakObj.~MyObjectWeakPtr(); 
} 

auto weakObj = *static_cast<MyObjectWeakPtr*>(
    luaL_checkudata(L, 1, "MyObject.Metatable")); 
luaL_argcheck(L, weakObj != nullptr, 1, "'MyObjectWeakPtr' expected"); 

// If you're using a weak_ptr, this is required!!!! If your userdata is a 
// shared_ptr, you can just act on the shared_ptr after luaL_argcheck() 
if (auto obj = weakObj.lock()) { 
    // You have a valid shared_ptr, the C++ object is alive and you can 
    // dereference like a normal shared_ptr. 
} else { 
    // The C++ object went away, you can safely garbage collect userdata 
} 

それはあなたのLUAでweak_ptr__gcメタメソッドの割り当てを解除することを忘れないことが重要ですあなたがいる限りのために生きているC++オブジェクトを維持する必要がある場合

使用shared_ptrなど、static_cast<>luaL_argcheck()

は、コードの重複REの多くを回避するために、マクロやテンプレートメタプログラミングを利用することを忘れないでくださいルアオブジェクトも存在します。weak_ptrを使うと、C++がオブジェクトを刈り取ることができ、それがluaの足元から消えても大丈夫です。オブジェクトの寿命がわからず、refcountによって自動的に管理する必要がある場合は、常にshared_ptrまたはweak_ptrのいずれかを使用してください。

ヒント:C++クラスをboost::enable_shared_from_thisまたはstd::enable_shared_from_thisから継承する場合は、shared_from_this()を使用できるようにしてください。

関連する問題