2016-07-09 53 views
3

首先,我指定使用Windows 10 64位和Haskell Platform 8.0.1。Haskell在Windows中使用GHCI的外部函數接口

我嘗試使用以下代碼在Windows中使用Haskell的FFI。

import Control.Monad 
import Data.Char 
import Foreign.C 

getCh :: IO Char 
getCh = liftM (chr . fromEnum) c_getch 
foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt 

main :: IO() 
main = getCh >>= \x -> print x 

在此之後,我可以用GHC

> ghc Examples.hs 
[1 of 1] Compiling Main    (Examples.hs, Examples.o) 
Linking Examples.exe ... 

編譯這很好,這完全運行。

> Examples.exe 
'1' 

(當我運行它後類型1)

然而,隨着GHCI出現問題。當我將它加載到ghci時,我收到了這些消息。

> ghci Examples.hs 
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help 
[1 of 1] Compiling Main    (Examples.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> main 

ByteCodeLink: can't find label 
During interactive linking, GHCi couldn't find the following symbol: 
    getch 
This may be due to you not asking GHCi to load extra object files, 
archives or DLLs needed by your current session. Restart GHCi, specifying 
the missing library using the -L/path/to/object/dir and -lmissinglibname 
flags, or simply by naming the relevant files on the GHCi command line. 
Alternatively, this link failure might indicate a bug in GHCi. 
If you suspect the latter, please send a bug report to: 
    [email protected] 

*Main> 

我嘗試加載「失蹤庫」,如「-lmsvcrt」,這需要使用conio.h,但結果是一樣悲觀。

> ghci -lmsvcrt Examples.hs 
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help 
[1 of 1] Compiling Main    (Examples.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> main 

ByteCodeLink: can't find label 
During interactive linking, GHCi couldn't find the following symbol: 
    getch 
This may be due to you not asking GHCi to load extra object files, 
archives or DLLs needed by your current session. Restart GHCi, specifying 
the missing library using the -L/path/to/object/dir and -lmissinglibname 
flags, or simply by naming the relevant files on the GHCi command line. 
Alternatively, this link failure might indicate a bug in GHCi. 
If you suspect the latter, please send a bug report to: 
    [email protected] 

*Main> 

GHCI可能加載庫,因爲當我嘗試加載錯誤的庫中,ghci的輸出有關的錯誤。

我嘗試其他一些東西,比如使用ghci Examples.hs -fobject-codeghci -lmsvcrt Examples.hs -fobject-code,甚至

ghci Examples.hs "-luser32" "-lgdi32" "-lwinmm" "-ladvapi32" "-lshell32" 
"-lshfolder" "-lwsock32" "-luser32" "-lshell32" "-lmsvcrt" "-lmingw32" 
"-lmingwex" "-luser32" "-lmingw32" "-lmingwex" "-lm" "-lwsock32" "-lgdi32" "-lwinmm" 

這是在ghc Examples.hs -v5找到。

不幸的是,沒有什麼適用於我的main,我無法找到任何其他途徑。

P.S.有沒有人知道如何在Windows中使用hSetBuffering(它在8年前發佈在ghc ticket #2189。是不是固定?)

+0

我只能告訴你兩兩件事沒有幫助:1.這個工程在Linux下就好使用'stdio.h中getchar',而無需指定一個圖書館,和2.你的方法看起來大致正確。 – crockeea

+0

@Eric在Linux中,在這種情況下不需要FFI,因爲hSetBuffering函數可以正常工作,並且通過使用該函數,我可以創建_Bufferless Input_。但是,這種方法不適用於Windows。 –

+0

我只提到你試圖用'getChar'鏈接的主要問題。我無法幫助您解決緩衝問題。 – crockeea

回答

1

這是因爲在Windows上沒有getchgetch是POSIX,POSIX在Windows上已被棄用。它仍然存在,但功能已被移到不同的名稱空間(將根名稱空間釋放到用戶程序中)。正如你可以看到MSDN說getch已被棄用https://msdn.microsoft.com/en-us/library/ms235446.aspx,並使用_getch來代替。

import Control.Monad 
import Data.Char 
import Foreign.C 

getCh :: IO Char 
getCh = liftM (chr . fromEnum) c_getch 
foreign import ccall unsafe "conio.h _getch" c_getch :: IO CInt 

main :: IO() 
main = getCh >>= \x -> print x 

將既編譯又解釋。

至於爲什麼使用getch時,它的工作原理編譯,而不是解釋:

MingW-w64項目已永遠不會被刪除過時的功能微軟

這樣

$ objdump -t /home/Tamar/ghc2/inplace/mingw/x86_64-w64-mingw32/lib/libmsvcr120.a | grep getch 
[ 7](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 getch 
[ 8](sec 5)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 __imp_getch 
[ 7](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 _getch 
[ 8](sec 5)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 __imp__getch 

getch被重定向到_getch,所以他們有兩個版本。這是MSVC++和GCC之間不兼容的根源。

微軟卻已經刪除它們

>dumpbin /exports C:\Windows\System32\msvcr120.dll | findstr getch 
     699 2BA 0006B8B4 _getch = _getch 

所以會發生什麼,當你鏈接對msvcrt

  1. 在編譯模式GCC和GHC會先挑靜態庫,恰好是一個導入庫文件libmsvcrt.dll.a。這是由於鏈接器(ld)的鏈接順序。

  2. 在解釋模式下,GHCi將總是優先於靜態庫的動態版本。原因是在重新鏈接期間(當您引入新的作用域或重新加載時必須發生),動態庫的速度要快得多,因爲我們不必在內部進行重定位和符號解析。還有一些東西我們仍然不能像弱符號或常見符號那樣正確支持,所以出於這些原因,我們只喜歡動態符號。

  3. GHCi 8.0.1不支持導入庫。因此,儘管您可以強制GHCi使用靜態庫(只需指定全名爲-l,例如-llibmsvcr.a),但它不起作用,因爲運行時加載程序不知道如何處理它。這在目前的GIT主然而支持,並可能會在8.0.2

+0

那麼'ghci --show-options'中的'-l'選項是什麼? GHCi用戶指南說:「額外的庫可以使用正常的-llib選項在命令行中指定。」 [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#extra-libraries] –

+0

@JunyoungClareJang'-l'用於向鏈接器指示您對圖書館。庫有不同的形式,你有靜態的''.a'擴展名,動態'.dll'或導入庫'.dll.a','.a'和.'.ib'。當你指定'-lfoo'時,你告訴鏈接器你需要庫'foo'。如果您有多個版本的圖書館,例如libfoo.dll,libfoo.a,libfoo.lib,鏈接器使用哪一個取決於各種標誌和搜索優先級。正如我之前所說的,GHCi目前總是比任何靜態版本更喜歡動態版本。 – Phyx

+1

這些並不神奇,我們必須積極編寫代碼來支持GHCi。因此Haskell運行時加載程序(RTS)目前還不支持一些功能。正如我所說,支持更多正在積極添加,如8.0.2中的導入庫支持https://phabricator.haskell.org/D1696 – Phyx