Come correttamente abbattuto in C# con un SORSO di interfaccia generato?
Domanda
Ho una molto grande e matura di codice C++ di base che sto cercando di utilizzare un SORSO a generare un C# interfaccia.Non riesco a cambiare l'attuale codice C++, ma possiamo utilizzare qualsiasi SORSO offre in modo di estendere/aggiornamento.Mi trovo di fronte a un problema in cui una funzione C++ che è scritto di seguito è causa di problemi in C#.
A* SomeClass::next(A*)
Il chiamante potrebbe fare qualcosa di simile:
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 )
...
}
Essenzialmente, l'iterazione attraverso un contenitore di elementi che, a seconda del loro tipo, fa qualcosa di diverso.Il SORSO generata C# per lo strato di "next" funzione purtroppo la seguente:
return new A();
Così la chiamata di codice in C# non è possibile determinare se l'oggetto restituito è in realtà una classe derivata o no, in realtà sembra essere sempre la classe base (che non ha senso).Ho incontrato diverse soluzioni:
- Utilizzare l' %estendere SORSO parola chiave per aggiungere un metodo su un oggetto e, in definitiva, chiamata dynamic_cast.Lo svantaggio di questo approccio, per come la vedo io, è che questo richiede di conoscere la gerarchia di ereditarietà.Nel mio caso è piuttosto grande e per me, questo è un problema di manutenzione.
- Utilizzare la %di fabbrica parola chiave per fornire il metodo e il derivato tipi e sono SORSO di generare automaticamente il dynamic_cast codice.Questa sembra essere una soluzione migliore che il primo, tuttavia, su uno sguardo più profondo richiede comunque di dare la caccia a tutti i metodi e tutte le possibili tipi derivati potrebbe tornare.Di nuovo, un enorme problema di manutenzione.Vorrei avere un collegamento doc per questo, ma non riesco a trovare uno.Ho scoperto questa funzionalità, cercando attraverso il codice di esempio fornito con SORSO.
- Creare un metodo C# per creare un'istanza di un oggetto derivato e trasferire il cPtr alla nuova istanza.Mentre considero questo goffo, funziona.Vedere un esempio qui sotto.
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; }
Sono queste le opzioni?E se io non sono disposto a scavare attraverso tutte le funzioni esistenti e di classe derivazioni, poi mi sono lasciato con la #3?Qualsiasi aiuto sarebbe apprezzato.
Soluzione 3
Abbiamo aggiunto le funzioni per l'interfaccia di ottenere lo specifico tipo di bisogno:
// .cpp
class foo : public bar {
}
///////////// part of swig
// .i (swig)
%extend foo {
static foo* GetFoo( bar* iObj ) {
return (foo*)iObj;
}
}
È un po ' noioso, perché prima doveva essere fatto per ogni classe, ma poi di nuovo, potrebbe essere girato in un SORSO macro.
Altri suggerimenti
Per impostazione predefinita SORSO genera C# e il codice Java che non supporta abbattuto per polimorfici i tipi di ritorno.Ho trovato un modo semplice per risolvere questo problema, fornito il codice C++ è un modo per identificare la classe di calcestruzzo del C++ istanze che vengono restituiti.Che è, la mia tecnica funziona solo se il C++ API si sono avvolgimento con il SORSO è qualcosa di simile a C# oggetto.GetType() o un Oggetto Java.getClass().
La soluzione è di aggiungere un C# classe intermedia metodo per creare un'istanza della classe concreta che il C++ si dice.Quindi, utilizzare %typemap(out) per dire a SORSO per utilizzare questa classe intermedia metodo per riportare le classi astratte.
Questo è un terribilmente concisa spiegazione, in modo da fare riferimento al mio articolo del blog che mostra come generare polimorfici C# e Java che è possibile abbattuto.Ha tutti i dettagli.
La terza soluzione nel post originale non funziona in Sorso 2.0 dove i costruttori sono privati (così l'Attivatore non riesce a trovare il costruttore).Quindi diversi BindingFlags devono essere utilizzati.Qui è una variante generica della terza soluzione accennato nel post originale:
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
);
}
}
Due SORSO wrapper Foo
e Bar where Bar : Foo
, è possibile provare ad abbattuto Foo
per Bar
come nell'esempio seguente:
Foo foo = new Bar();
Bar bar = SwigHelper.CastTo<Bar>(foo, false);