Подтвердить что ты не робот

Вызов функции Haskell в .NET.

Я хочу использовать функцию Haskell со следующим типом:: string -> string из программы С#.

Я хочу использовать hs-dotnet для соединения обоих миров. Автор утверждает, что это возможно, но не дает выборки этого случая. Единственными предоставленными примерами являются те, которые используют .NET из Haskell.

Есть ли образец этого использования или как его использовать? (Я использовал .NET Reflector на сборке мостов, но я ничего не понял.)

4b9b3361

Ответ 1

В то время как ваш путь работает, стоит отметить, что ваши плоды, к которым вы столкнулись, к сожалению (а не ошибка в GHC): ((Предполагается, что вы использовали документацию GHC при создании библиотеки DLL и загрузили RTS в основной DLL).

В первой части проблемы с распределением памяти, которые вы представляете, существует гораздо более простой способ обработки С#, что является небезопасным кодом. Любая память, выделенная в небезопасном коде, будет выделена вне управляемой кучи. Таким образом, это отрицает необходимость использования трюков C.

Вторая часть - это использование LoadLibrary в С#. Причина P/Invoke не может найти ваш экспорт довольно просто: в вашем коде Haskell вы объявили инструкцию экспорта с помощью ccall, в то время как в .NET стандартное соглашение об именах stdcall, которое также является стандартом для вызовов Win32.

stdcall и ccall имеют разные имена и сопоставления имен в процессе очистки аргументов.

В частности, GHC/GCC будет экспортировать "wEval", а .NET по умолчанию будет искать "_wEval @4". Теперь, когда это довольно легко исправить, просто добавьте CallingConvention = CallingConvention.Cdecl.

Но используя это соглашение о вызове, вызывающему нужно очистить стек. Поэтому вам понадобится дополнительная работа. Теперь, предполагая, что вы собираетесь использовать это только в Windows, просто экспортируйте свою функцию Haskell как stdcall. Это упрощает ваш код .NET и делает

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern string myExportedFunction(string in);

почти правильно.

Какое правильное будет, например,

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public unsafe static extern char* myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);

Больше нет необходимости в loadLibrary или тому подобное. И чтобы получить управляемую строку, просто используйте

String result = new String(myExportedFunction("hello"));

например.

Можно было бы подумать, что

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
[return : MarshalAs(UnmanagedType.LPWStr)]
public static extern string myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);

тоже должен работать, но это не так, поскольку Маршаллер ожидает, что String будет выделен CoTaskMemAlloc и вызовет CoTaskMemFree на нем и сбой.

Если вы хотите остаться полностью в управляемой земле, вы всегда можете сделать

[DllImport("foo.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr myExportedFunction([MarshalAs(UnmanagedType.LPWStr)]string in);

а затем его можно использовать как

string result = Marshal.PtrToStringUni(myExportedFunction("hello"));

Инструмент доступен здесь http://hackage.haskell.org/package/Hs2lib-0.4.8

Обновление. Там была какая-то большая проблема, которую я недавно обнаружил. Мы должны помнить, что тип String в .NET является неизменным. Поэтому, когда маршаллер отправляет его с кодом Haskell, мы получаем CWString копию оригинала. У нас есть, чтобы освободить это. Когда GC выполняется в С#, это не повлияет на CWString, которая является копией.

Однако проблема заключается в том, что когда мы освобождаем ее в коде Haskell, мы не можем использовать freeCWString. Указатель не был выделен с помощью C (msvcrt.dll) alloc. Есть три способа (которые я знаю), чтобы решить эту проблему.

  • используйте char * в коде С# вместо String при вызове функции Haskell. Затем у вас есть указатель на свободный, когда вы вызываете return, или инициализируете указатель, используя fixed.
  • импортировать CoTaskMemFree в Haskell и освободить указатель в Haskell
  • используйте StringBuilder вместо String. Я не совсем уверен в этом, но идея в том, что, поскольку StringBuilder реализован как собственный указатель, Marshaller просто передает этот указатель на ваш код Haskell (который также может его обновить). Когда GC выполняется после возврата вызова, StringBuilder должен быть освобожден.

Ответ 2

Так же, как обновление, я решил проблему, создав DLL файл haskell и таким образом соединив два мира.

Если вы хотите использовать тот же путь, не забудьте использовать ::CoTaskMemAlloc для распределения данных для мира .net. Еще одна проблема заключается в использовании LoadLibrary/GetProcAdress, по какой-то неизвестной причине импорт не работает автоматически так, как они должны быть. Более подробно в деталях статьи, чтобы помочь вызвать haskell из .net.

Ответ 3

Вы, конечно же, можете назвать Haskell с C - вы используете "внешний экспорт" в файле Haskell, а GHC генерирует заголовок C, который вы затем можете импортировать и использовать для вызова в Haskell с C.

Я не видел, чтобы это было сделано для привязок .NET. Поэтому я думаю, что лучше всего спросить как автора - Sigbjorn, так и примеры из haskell-cafe @.

Ответ 4

Если вы хотите Haskell на .NET, просто используйте F #.