9

私は多くのことに依存する API (特定の GUI ライブラリ) を持っていますstd::shared_ptr。つまり、多くの場合、関数パラメーターとして使用され、他のオブジェクト内に格納されます。たとえば、スプリッターやボックスなどのコンテナー ウィジェットは、子ウィジェットを に格納しますshared_ptr。次に、この API を luabind を介して Lua にマップしたいと思います。理想的な世界では、Luabind は shared_ptrs で新しいオブジェクトを作成し、それらを shared_ptr パラメーターを受け取る関数に直接渡すことができるようにします。これは、たとえば次のような単一のクラスで機能するようです。

luabind::class_<Button, std::shared_ptr<Button>>("Button")

そのように宣言している間、 のような関数を公開して使用できますvoid foo(std::shared_ptr<Button> const&)

luabind のマニュアルでは、クラスの階層を使用するには、階層内のすべてのクラスに同じ shared_ptr テンプレート インスタンスを使用する必要があると記載されています。

luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>>("BaseWidget"),
luabind::class_<Button, BaseWidget, std::shared_ptr<BaseWidget>>("Button")

これで、呼び出すことができなくなりましfooた。Lua から関数を見つけることができなくなります。luabind がshared_ptrs でのボタンの受け渡しを引き続きサポートするようにすることはできますか? また、階層内のすべてのクラスに対して、基本クラスのポインターに変換できるのではなく、同じスマート ポインターを使用するように luabind が義務付けている理由を知りたいです。

4

1 に答える 1

7

それが機能するには、次のように派生クラスをバインドする必要があると思います。

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")

例えば:

class BaseWidget
{
public:
    static void Bind2Lua(lua_State* l)
    {
        luabind::module(l)
        [
            luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>> ("BaseWidget")
            .def(luabind::constructor<>())
        ];
    }

    virtual ~BaseWidget()
    {

    }
};

class Button : public BaseWidget
{
public:
    static void Bind2Lua(lua_State* l)
    {
        luabind::module(l)
        [
            luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")
            .def(luabind::constructor<>())
            .def("Click", &Button::Click)
        ];
    }

    void Click()
    {
        std::cout << "Button::Click" << std::endl;
    }
};

これで、shared_ptr で使用できます。

class Action
{
public:
    void DoClick(const std::shared_ptr<Button>& b)
    {
        // perform click action
        b->Click();
    }

    static void Bind2Lua(lua_State* l)
    {
        luabind::module(l)
        [
            luabind::class_<Action> ("Action")
            .def(luabind::constructor<>())
            .def("DoClick", &Action::DoClick)
        ];
    }
};

ルアで:

b = Button()

a = Action()

a:DoClick(b)

その理由は、luabind が整数の type-id システムを使用するためです (より正確には、inheritance.hpp で定義されている std::size_t)。
次の関数を使用して、登録されている任意のタイプのタイプ ID を取得できます。

luabind::detail::static_class_id<T>(nullptr);

ここで、T は登録されたクラスです。
私のデモプログラムでは、次のとおりです。

  • ベースウィジェット = 3
  • std::shared_ptr< BaseWidget > = 6
  • ボタン = 4
  • std::shared_ptr< ボタン > = 7
  • アクション = 5

そのため、lua から DoClick を呼び出すと、instance_holder.hpp 内のテンプレート クラス pointer_holder の get メンバーが呼び出されます。

std::pair<void*, int> get(class_id target) const
{
    if (target == registered_class<P>::id)
        return std::pair<void*, int>(&this->p, 0);

    void* naked_ptr = const_cast<void*>(static_cast<void const*>(
        weak ? weak : get_pointer(p)));

    if (!naked_ptr)
        return std::pair<void*, int>((void*)0, 0);

    return get_class()->casts().cast(
        naked_ptr
      , static_class_id(false ? get_pointer(p) : 0)
      , target
      , dynamic_id
      , dynamic_ptr
    );
}

ご覧のとおり、ターゲット クラスが登録されたものと同じでない場合、キャストを実行しようとします。
ここからが興味深いところです。Button クラスを次のように宣言した場合

luabind::class_<Button, BaseWidget,std::shared_ptr<BaseWidget>>("Button")

インスタンスは BaseWidget への shared_ptr として保持されるため、キャスト関数は BaseWidget (3) から std::shared_ptr< Button > (7) にキャストしようとしますが、失敗します。luabind がベースから派生への変換をサポートしていれば機能する可能性がありますが、そうではないようです。

ただし、Button クラスを次のように宣言した場合

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")

インスタンスはボタンへのshared_ptrとして保持され、ターゲットIDは登録されたタイプと一致します。get 関数は最初のリターンで分岐し、キャストに煩わされることはありません。

ここで使用した自己完結型のプログラムは、pastebin にもあります。

そして、何が起こっているかを見るために設定できる興味深いブレークポイントのリストです (luabind バージョン 900):

  • instance_holder.hpp の 94 行目 (pointer_holder::get の最初の行)
  • instance.cpp の 143 行目 (cast_graph::impl::cast の最初の行)
于 2012-06-27T14:42:36.523 に答える