2014-12-03 14 views
10

Durante il tentativo di dimostrare a un collega che è possibile utilizzare classi C++ da F #, ho trovato la seguente dimostrazione di concetto. Il primo frammento è il codice che ha fornito per la sfida e il frammento di codice seguente è la mia implementazione in F #.Possibile errore PInvoke F # interattivo


namespace testapp { 
    struct trivial_foo { 
     int bar; 
     __declspec(dllexport) void set(int n) { bar = n; } 
     __declspec(dllexport) int get() { return bar; } 
    } 
} 

open System.Runtime.InteropServices 

type TrivialFoo = 
    struct 
     val bar: int 
     new(_bar: int) = { bar = _bar } 
    end 

[<DllImport("Win32Project2.dll", EntryPoint="[email protected][email protected]@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)>] 
extern int trivial_foo_get(TrivialFoo& trivial_foo) 

[<DllImport("Win32Project2.dll", EntryPoint="[email protected][email protected]@@[email protected]", CallingConvention = CallingConvention.ThisCall)>] 
extern void trivial_foo_set(TrivialFoo& trivial_foo, int bar) 

type TrivialFoo with 
    member this.Get() = trivial_foo_get(&this) 
    member this.Set(bar) = trivial_foo_set(&this, bar) 

Quando il debug in Visual Studio o eseguire come programma autonomo, questo funziona prevedibile: TrivialFoo.Get restituisce il valore di bar e TrivialFoo.Set assegna ad esso. Se eseguito da F # Interactive, tuttavia, TrivialFoo.Set non imposterà il campo. Sospetto che possa avere qualcosa a che fare con l'accesso alla memoria gestita dal codice non gestito, ma questo non spiega perché accade solo quando si usa F # Interactive. Qualcuno sa cosa sta succedendo qui?

+0

Bene, questo non è il modo di fare interop lo stesso. Dichiarare una struttura POD con funzioni non + imember. Immagino che il tuo codice stia fallendo perché la struttura è considerata un parametro in te, ma in ogni caso è tutto sbagliato. Questo non è il modo in cui si interpola con le classi. –

+0

Un altro problema tipico potrebbe essere che la DLL non è in% PATH% quando si utilizza FSI anziché .exe. – weismat

+0

Soprattutto perché l'ombra FSI copia gli assembly per impostazione predefinita, quindi il codice potrebbe essere in esecuzione da una cartella temporanea da qualche parte. – marklam

risposta

1

Non credo che questa prova di concetto sia una buona prova di interoperabilità. Potrebbe essere meglio creare definizioni di esportazione DLL dal progetto C++ e utilizzare invece i nomi declassificati.

Come PoC: F # crea MSIL che si inserisce nella CLI, in modo che possa interagire con any other CLI language out there. Se ciò non è sufficiente e si desidera l'interoperabilità da rete a rete, considerare l'utilizzo di COM o, come già detto, le definizioni di esportazione DLL sul C++. Personalmente non proverei ad utilizzare le definizioni di classe C++ come suggerite qui, ci sono modi più semplici per farlo.

In alternativa, è sufficiente modificare il progetto C++ in un progetto .NET C++ e accedere alle classi direttamente da F #, pur mantenendo la potenza del C++.

Naturalmente, ci si potrebbe chiedere ancora perché l'esempio non viene eseguito in FSI. Si può vedere un accenno di risposta eseguendo il seguente:

> System.IO.Directory.GetCurrentDirectory();; 
val it : string = "R:\TMP" 

Per risolvere questo problema, si ha una miriade di opzioni:

  • copia Win32Project2.dll a quella directory
  • aggiungere qualunque percorso è a PATH
  • uso un percorso assoluto
  • utilizzare una costante della fase di compilazione
  • o utilizzare una e variabili nvironment (il percorso sarà ampliato)
  • individuare in modo dinamico la dll e si legano in modo dinamico ad esso (complesso)

copia è probabilmente il più facile di queste soluzioni.

Poiché FSI è da intendersi come REPL, potrebbe non essere la soluzione migliore per questo tipo di attività che richiedono più progetti, librerie o configurazioni altrimenti complesse. Potresti considerare di votare su this FSI request for support for #package per importare i pacchetti NuGet, che potrebbero essere utilizzati per facilitare tali attività.

0

La controparte di una struttura C++ in F # non è necessariamente una struttura. In C++, l'unica differenza tra classi e strutture risiede nelle loro restrizioni di accesso predefinite.

In F #, le strutture vengono utilizzate per i tipi di valore, le classi vengono utilizzate per i tipi di riferimento. Un problema con i tipi di valore è che sono pensati per essere utilizzati come valori immutabili e le copie temporanee vengono spesso create in modo silenzioso.

Il problema che si sta osservando è coerente con quello scenario. Per qualche ragione, F # interactive crea una copia della tua struct e passa un riferimento a questo. Il codice C++ modifica quindi la copia, lasciando intatto l'originale.

Se si passa all'utilizzo di una classe, assicurarsi di appuntare l'istanza prima di consentire al codice nativo di utilizzarla, oppure si può finire in una situazione in cui il garbage collector lo sposta dopo che il codice nativo ha ottenuto un riferimento.

Problemi correlati