Question

J'ai un très grand et mature base de code C ++ que je suis en train d'utiliser SWIG sur pour générer une interface C # pour. Je ne peux pas changer le code C ++ réel lui-même, mais nous pouvons utiliser ce que SWIG offre de la manière d'étendre / mettre à jour. Je suis confronté à un problème où une fonction C ++ qui est écrit ci-dessous est à l'origine des problèmes en C #.

A* SomeClass::next(A*)

L'appelant peut faire quelque chose comme:

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 )
    ...
}

Pour l'essentiel, itérer à travers un récipient d'éléments qui, selon leur vrai type, fait quelque chose de différent. La SWIG générée couche C # pour la fonction « suivant » fait malheureusement ce qui suit:

return new A();

Ainsi, le code d'appel en C # ne peut pas déterminer si l'objet retourné est en fait une classe dérivée ou non, il semble en fait toujours la classe de base (ce qui est logique). Je suis venu à travers plusieurs solutions:

  1. Utilisez le mot-clé% extension SWIG ajouter une méthode sur un objet et, finalement, appeler dynamic_cast. L'inconvénient de cette approche, comme je le vois, est que cela vous oblige à connaître la hiérarchie d'héritage. Dans mon cas, il est assez grand et je vois c'est comme un problème de maintenance.
  2. Utilisez le mot-clé usine% pour fournir la méthode et les types dérivés et ont SWIG générer automatiquement le code dynamic_cast. Cela semble être une meilleure solution que la première, mais sur un regard plus profond, il vous faut encore de traquer toutes les méthodes et tous les types dérivés possibles, il pourrait revenir. Encore une fois, un énorme problème de maintenance. Je voudrais avoir un lien de doc pour cela, mais je ne peux pas trouver un. J'ai découvert cette fonctionnalité en regardant à travers l'exemple de code qui vient avec SWIG.
  3. Créer une méthode C # pour créer une instance de l'objet dérivé et transférer le cPtr à la nouvelle instance. Bien que je considère que ce maladroit, il ne fonctionne pas. Voir un exemple ci-dessous.
    public static object castTo(object fromObj, Type toType)
    {
        object retval = null;

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

        // make sure it actually is what we think it is
        if (fromObj.GetType().IsInstanceOfType(toObj))
        {
            return toObj;
        }

        return retval;
    }

Sont-ils vraiment les options? Et si je ne suis pas prêt à fouiller dans toutes les fonctions existantes et dérivations de classe, alors je suis parti avec # 3? Toute aide serait appréciée.

Était-ce utile?

La solution 3

Nous avons ajouté des fonctions à l'interface pour obtenir le type spécifique nécessaire:

// .cpp
class foo : public bar {
}

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

Il est un peu fastidieux, beause il fallait faire pour toutes les classes, mais là encore, il pourrait être transformé en une macro SWIG.

Autres conseils

La troisième solution dans le message original ne fonctionne pas dans Swig 2.0 où les constructeurs sont privés (donc l'Activator ne peut pas trouver le constructeur). Par conséquent, différentes BindingFlags doivent être utilisés. Voici une variante générique de la troisième solution mentionnée dans le message original:

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
        );
    }
}

Étant donné deux emballages SWIG Foo et Bar where Bar : Foo, vous pouvez maintenant essayer de Foo baissés à Bar comme dans l'exemple suivant:

Foo foo = new Bar();
Bar bar = SwigHelper.CastTo<Bar>(foo, false);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top