3

SWIG を使用して C# インターフェイスを生成しようとしている非常に大規模で成熟した C++ コード ベースがあります。実際の C++ コード自体を変更することはできませんが、SWIG が提供するコードを拡張/更新するために使用することはできます。以下のように記述された C++ 関数が C# で問題を引き起こしているという問題に直面しています。

A* SomeClass::next(A*)

呼び出し元は次のようなことをするかもしれません:

A* acurr = 0;
while( (acurr = sc->next(acurr)) != 0 ){
    if( acurr isoftype B ){
        B* b = (B*)a;
        ...do some stuff with b..
    }
    elseif( acurr isoftype C )
    ...
}

基本的に、真の型に応じて異なる処理を行う要素のコンテナーを反復処理します。SWIG が生成した "next" 関数用の C# レイヤーは、残念ながら次のことを行います。

return new A();

そのため、C# の呼び出しコードは、返されたオブジェクトが実際に派生クラスであるかどうかを判断できず、実際には常に基本クラスのように見えます (これは理にかなっています)。私はいくつかの解決策に出くわしました:

  1. %extend SWIG キーワードを使用してオブジェクトにメソッドを追加し、最終的に dynamic_cast を呼び出します。私が見ているように、このアプローチの欠点は、継承階層を知る必要があることです。私の場合、それはかなり大きく、これはメンテナンスの問題だと思います。
  2. %factory キーワードを使用してメソッドと派生型を提供し、SWIG に dynamic_cast コードを自動的に生成させます。これは最初のソリューションよりも優れているように見えますが、詳しく見てみると、すべてのメソッドと、それが返す可能性のあるすべての派生型を探し出す必要があります。繰り返しますが、大きなメンテナンスの問題です。このドキュメントのリンクがあればいいのですが、見つかりません。SWIG に付属のサンプル コードを調べて、この機能を知りました。
  3. 派生オブジェクトのインスタンスを作成し、cPtr を新しいインスタンスに転送する C# メソッドを作成します。これは不器用だと思いますが、うまくいきます。以下の例を参照してください。
    public static object castTo(object fromObj, Type toType)
    {
        オブジェクト retval = null;

        BaseClass fromObj2 = fromObj as BaseClass;
        HandleRef hr = BaseClass.getCPtr(fromObj2);
        IntPtr cPtr = hr.Handle;
        object toObj = Activator.CreateInstance(toType, cPtr, false);

        // 実際に私たちが考えているものであることを確認してください
        if (fromObj.GetType().IsInstanceOfType(toObj))
        {
            オブジェクトに戻ります。
        }

        retval を返します。
    }

これらは本当にオプションですか?そして、既存のすべての関数とクラスの派生を掘り下げる気がない場合は、#3 が残っていますか? どんな助けでも大歓迎です。

4

3 に答える 3

4

デフォルトでは、SWIG は、ポリモーフィックな戻り値の型のダウンキャストをサポートしない C# および Java コードを生成します。C++ コードに、返されるC++ インスタンスの具象クラスを識別する方法があれば、これを解決する簡単な方法を見つけました。つまり、SWIG でラップしている C++ API が C# object.GetType() または Java Object.getClass() に似たものを持っている場合にのみ、私の手法が機能します。

解決策は、C# 中間クラス メソッドを追加して、C++ が言う具象クラスをインスタンス化することです。次に、 %typemap(out) を使用して、抽象クラスを返すときにこの中間クラス メソッドを使用するよう SWIG に指示します。

これは非常に簡潔な説明なので、ダウンキャストできるポリモーフィックな C# と Java を生成する方法を示す私のブログ記事を参照してください。それはすべての詳細を持っています。

于 2011-04-11T01:02:54.763 に答える
2

元の投稿の3番目の解決策は、コンストラクターがプライベートであるSwig 2.0では機能しません(したがって、アクティベーターはコンストラクターを見つけることができません)。したがって、異なるBindingFlagsを使用する必要があります。これは、元の投稿で言及されている3番目のソリューションの一般的なバリアントです。

public class SwigHelper
{
    public static T CastTo<T>(object from, bool cMemoryOwn)
    {
        System.Reflection.MethodInfo CPtrGetter = from.GetType().GetMethod("getCPtr", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
        return CPtrGetter == null ? default(T) : (T) System.Activator.CreateInstance
        (
            typeof(T),
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
            null,
            new object[] { ((HandleRef) CPtrGetter.Invoke(null, new object[] { from })).Handle, cMemoryOwn },
            null
        );
    }
}

2つのSWIGラッパーFooとが与えられた場合、次の例のようにダウンキャストをBar where Bar : Foo試みることができます。FooBar

Foo foo = new Bar();
Bar bar = SwigHelper.CastTo<Bar>(foo, false);
于 2011-08-23T11:07:10.390 に答える
1

必要な特定のタイプを取得するために、インターフェイスに関数を追加しました。

// .cpp
class foo : public bar {
}

///////////// part of swig
// .i (swig)
%extend foo {
    static foo* GetFoo( bar* iObj ) {
         return (foo*)iObj;
   }
}

クラスごとに実行する必要があるため、少し面倒ですが、SWIG マクロに変換することもできます。

于 2011-01-07T04:26:36.863 に答える