2014-12-03 13 views
10

Podczas próby udowodnienia kolegom, że można używać klas C++ z F #, wymyśliłem następujący dowód koncepcji. Pierwszy fragment to kod podany dla wyzwania, a poniższy fragment kodu jest moją implementacją w języku F #.Możliwe F # Interactive PInvoke bug


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) 

Gdy debugowanie w Visual Studio lub działać jako samodzielny program, to działa przewidywalnie: TrivialFoo.Get zwraca wartość bar i TrivialFoo.Set przypisuje do niego. Po uruchomieniu z F # Interactive jednak TrivialFoo.Set nie ustawi pola. Podejrzewam, że może to mieć coś wspólnego z dostępem do zarządzanej pamięci z niezarządzanego kodu, ale to nie wyjaśnia, dlaczego tak się dzieje tylko przy użyciu F # Interactive. Czy ktoś wie, co tu się dzieje?

+0

Cóż, to nie jest sposób na współdziałanie w każdym razie. Deklaracja POD struct z funkcjami non + piąta. Domyślam się, że kod zawodzi, ponieważ struktura jest uważana za paramatyczną metodę, ale w każdym razie wszystko jest w porządku. Nie tak postępujesz w klasach. –

+0

Innym typowym problemem może być to, że biblioteka DLL nie jest w% PATH% podczas używania FSI zamiast .exe. – weismat

+0

Zwłaszcza, że ​​domyślnie FSI kopiuje kopie złożeń, więc twój kod może być uruchomiony z tymczasowego folderu gdzieś. – marklam

Odpowiedz

1

Nie sądzę, że ten dowód koncepcji jest dobrym dowodem na interoperacyjność. Lepiej możesz stworzyć definicje eksportu DLL ze swojego projektu C++ i zamiast tego użyć nazw de-decorated.

Jako PoC: F # tworzy MSIL, który pasuje do interfejsu CLI, dzięki czemu może współpracować z any other CLI language out there. Jeśli to nie wystarcza i chcesz mieć współdziałanie natywnej sieci, rozważ użycie COM lub jak wspomniano wyżej, definicje eksportu DLL do twojego C++. Osobiście nie próbowałbym interakcji z definicjami klasy C++ w sposób sugerowany tutaj, są o wiele łatwiejsze sposoby, aby to zrobić.

Alternatywnie, po prostu zmień swój projekt C++ w projekt .NET C++ i możesz uzyskać dostęp do klas bezpośrednio z F #, mając jednocześnie moc C++.

Oczywiście, nadal możesz się zastanawiać, dlaczego przykład nie działa w FSI. Można zobaczyć podpowiedź odpowiedź wykonując następujące czynności:

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

Aby rozwiązać ten problem, masz mnóstwo opcji:

  • kopię Win32Project2.dll do tego katalogu
  • dodać cokolwiek ścieżka jest do PATH
  • bezwzględną ścieżki
  • wykorzystać podczas kompilacji stałą
  • albo używać e nvironment zmienna (ścieżka ma być rozszerzona)
  • dynamicznie zlokalizować dll dynamicznie wiążą się z nim (kompleks)

kopiowanie jest prawdopodobnie najprostszy z tych rozwiązań.

Ponieważ FSI ma być REPL, może nie być najlepiej dostosowane do tego rodzaju zadań, które wymagają wielu projektów, bibliotek lub innych złożonych konfiguracji. Możesz rozważyć głosowanie na numer this FSI request for support for #package w celu zaimportowania pakietów NuGet, które mogą być wykorzystane do ułatwienia takich zadań.

0

Odpowiednik struktury C++ w F # niekoniecznie jest strukturą. W C++ jedyna różnica między klasami i strukturami polega na ich domyślnych ograniczeniach dostępu.

W języku F #, w przypadku typów wartości używane są struktury, a w przypadku typów odniesienia używane są klasy. Jednym z problemów z typami wartości jest to, że mają one być używane jako wartości niezmienne, a kopie tymczasowe są często tworzone cicho.

Problem, który obserwujesz, jest zgodny z tym scenariuszem. Z jakiegoś powodu funkcja F # interactive tworzy kopię struktury i przekazuje odwołanie do niej. Kod C++ modyfikuje kopię, pozostawiając oryginał nietknięty.

Jeśli przełączysz się na używanie klasy, upewnij się, że przypniesz instancję przed zezwoleniem na używanie jej przez natywny kod lub możesz znaleźć się w sytuacji, w której moduł czyszczenia pamięci przeniesie go po uzyskaniu przez natywny kod odwołania do niego.

Powiązane problemy