0

SWIG を使用して C++ コードを Lua にバインドしています。これまでのところ問題ないように見えますが、Lua 内から単一のユーザーデータを「チート」して拡張し、カスタム フィールドやメソッドなどを追加する必要があります。

SWIG のディレクティブ内で作業しているときに、それを達成する方法が見つかりません。ラッパー コードのどこでマジックが行われるかはわかっていますが、__index と __newindex がどのように機能するかは完全にはわかりません。また、SWIG は __setitem と __getitem を使用しており、「/* NEW: __setitem() fn を検索します。これはユーザーが提供する set fn */ です」とコメントされています。これが何を意味するのかもわかりません。最後に、私の環境では、各ビルドの前に SWIG ディレクティブを C++ ラッパーにバインドするスクリプトが自動的に呼び出されるため、再コンパイルまたは Lua バインディングの追加を選択した場合、後でソースを変更するのは非常に面倒です。

これまでのところ、私の唯一の結論は、この機能が文書化されている toLua++ を使用することです。多くの移行作業を避けるのを手伝ってください!

編集: 5.2 で「lua_setuservalue」API 呼び出しも見つかりました。便利なようですが、すべての SWIG バインディング コードのどこで呼び出すかわかりません。

編集:物事を片付けるには:

C関数を持ってオブジェクトを作成します

SoundObj *loadSound(const char *name)
{
    return g_audio->loadSound(name); // Returns SoundObj pointer
}

この関数は、そのプロトタイプを *.i swig 定義に含めることによって、SWIG によってバインドされます。

Lua では、次のようなコードを記述します。

sound = audio.loadSound("beep.mp3")
sound.scale = 0.2
sound:play()

編集:前進!Scholii の指示に従い、次の Lua コードを書きました。ここで、「ground」はバインドされた C++ コードを使用して以前に取得されたユーザーデータです。コードは __index と __newindex の swig のバージョンを格納し、次に別の ("_other") テーブルを最初にクエリするこれらの関数を再作成します。

ここでの私の現在の問題は、「_other」テーブルに格納された新しい値が、そのタイプのすべてのユーザーデータ オブジェクト間で共有されることです。つまり、ground.nameFoo = "ha!" の場合、他のすべてのオブジェクトには "ha!" を格納する nameFoo フィールドがあります。これを修正するにはどうすればよいですか?

mt = getmetatable(ground)
mt._other = {}
mt.__oldindex = mt.__index
mt.__oldnewindex = mt.__newindex

mt.__index = function(tbl, key)
    if mt.__oldindex(tbl, key) == nil then
        if mt._other[key] ~= nil then
            return mt._other[key]
        end
    else
        return mt.__oldindex(tbl, key)
    end
end

mt.__newindex = function(tbl, key, val)
    if mt.__oldnewindex(tbl, key, val) == nil then
        mt._other[key] = val
    end
end

編集: この回答からソリューションを実装しました: Add members dynamic to a class using Lua + SWIG

問題は、オブジェクト タイプがユーザーデータではなく、テーブルになっていることです。つまり、ユーザーデータを引数として取る他のバインドされた C++ 関数に引数として有機的に渡すことができなくなりました。これに対する解決策はありますか?そして、ユーザーデータ オブジェクトを返すすべての関数に対してこれを行う必要があります。

4

3 に答える 3

1

つまり、C++ に SoundObject クラスがあり、それを Lua にエクスポートします (SWIG 経由か tolua++ 経由か手動かは関係ありません)。アプリケーションで、SoundObject のインスタンスを作成し、プロパティ (Lua 内) とメソッド (Lua 内) を追加する lua スクリプトを実行すると、そのプロパティを使用して C++ からそのメソッドを呼び出せるようになります。何かのようなもの:

-- Lua script: 
sound = audio.loadSound("beep.mp3")
sound.scale = 0.2   -- this property exported by SWIG
sound.loop = true   -- this is Lua only
sound.effect = function (self, a) print(a) end  -- this is Lua only
sound:play()        -- this method exported by SWIG

// C++ pseudocode:
bind SoundObject* load_sound(string) to "audio.loadSound"
run Lua script 

また、play() メソッドで追加のプロパティとメソッド (ループや効果など) を調査し、何かを実行する必要があります。想像できませんが、うまくいけば、これにより、問題をより明確に表現する方法がわかります。

于 2014-01-06T23:34:54.077 に答える
1

私はここでずさんになります: Lua では、テーブルは別のテーブルをメタテーブルとして使用することで "継承" します。したがって、C++ Foo を Lua にエクスポートし、Foo から派生した Lua "クラス" が必要な場合は、テーブルを作成し、そのメタテーブルを Foo に設定できます。次に、テーブルに存在しないフィールドで新しいテーブルにアクセスすると、メタテーブルが検索され、そこにあるかどうかが確認されます。疑似コードの例:

baseTable = {a=123}
assert( getmetatable(baseTable) == nil )
derived={b=456}
assert(derived.a == nil)
setmetatable(derived, baseTable )
assert(derived.a == 123)

baseTable は SWIG 経由で Lua にエクスポートされた C++ クラスです。そのメタテーブルは変更できませんが、Lua で作成したテーブルのメタテーブルは変更できます。そのため、SWIG コードに変更を加えたり、SWIG ディレクティブを使用したりする必要はありません。Lua で操作するだけで済みます。例:

-- define Foo class:
Foo = {}
Foo.__index = Foo -- make Foo a Lua "class"
setmettable(Foo, YourCppBaseClass) -- derived from your C++ class
-- instantiate it:
foo = {}
setmetatable(foo, Foo) -- foo is of "class" Foo
于 2014-01-06T05:41:49.643 に答える
0

私は Schollii の例に従って解決策を作成しましたが、犠牲は少なくなりました。これはユーザーデータをテーブルに変換しないため、まだユーザーデータ型があります (ユーザーデータを受け取る他の C++ 関数に渡すことができます)。Schollii のソリューションは、効果的にユーザーデータをテーブル ラッパーに変えました。

したがって、最初の行は変数「ground」を使用します。これは、SWIG によってラップされたユーザーデータ インスタンスです。実行の開始時にこれを行うと、そのタイプのすべてのインスタンスの「グラウンド」ユーザーデータ タイプのメタテーブルが変更されますが、各インスタンスは、ユーザーデータのメモリ位置によってインデックス付けされた個人テーブルを保持します。このテーブルは _G._udTableReg テーブルにあります。

-- Store the metatable and move the index and newindex elsewhere
mt = getmetatable(ground)
mt.__oldindex = mt.__index
mt.__oldnewindex = mt.__newindex

-- Store the global registry of tables associated with userdata, make a pointer to it in the metatable
_G._udTableReg = {}
mt._udTableReg = _G._udTableReg

-- Rewrite the new index function that looks in the udTableReg using 'self' as index before proceeding to use the old index as backup
mt.__index = function(self, key)
    local ret;
    local privateTable = mt._udTableReg[self]

    -- If the private table exists and has key, return key
    if privateTable ~= nil and privateTable[key] ~= nil then
        ret = privateTable[key]
    -- Use the old index to retrieve the original metatable value
    else ret = mt.__oldindex(self, key) end

    if ret == nil then return 0
    else return ret end
end

-- Try to assign value using the original newindex, and if that fails - store the value in
mt.__newindex = function(self, key, val)
    -- If old newindex assignment didn't work
    if mt.__oldnewindex(self, key, val) == nil then
        -- Check to see if the custom table for this userdata exists, and if not - create it
        if mt._udTableReg[self] == nil then
            mt._udTableReg[self] = {}
        end
        -- Perform the assignment
        mt._udTableReg[self][key] = val
    end
end

この Lua コードを配置するのに最適な場所、または実際に既存の変数を使用せずにユーザーデータのメタテーブルを取得するより洗練された方法を取得する方法をまだ見つけていません。

于 2014-01-06T22:17:28.683 に答える