2012-04-12 18 views
7

Tôi gặp vấn đề liên quan đến FFI trong Haskell và chế độ tương tác của GHC.GHCi không hoạt động với khai báo xuất khẩu FFI/thư viện được chia sẻ

(Nguồn cũng có sẵn thông qua một gist):

FFISo.hs:

{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE ForeignFunctionInterface #-} 
module Main where 

import qualified Data.ByteString.Char8 as B 

foreign import ccall "callMeFromHaskell" 
    callMeFromHaskell :: IO() 

foreign export ccall callMeFromC :: IO() 
callMeFromC :: IO() 
callMeFromC = B.putStrLn "callMeFromC" 

main :: IO() 
main = do 
    B.putStrLn "main" 
    callMeFromHaskell 
    return() 

c.c:

#include <stdio.h> 

void callMeFromC(void); 

void callMeFromHaskell(void) 
{ 
    printf("callMeFromHaskell\n"); 
    callMeFromC(); 
} 

Makefile:

GHC_OPT := -Wall -O2 -fno-warn-unused-do-bind 

all: ffiso 

test: ffiso 
    ./$< 

ffiso: FFISo.hs c.c 
    ghc --make $(GHC_OPT) $^ -o [email protected] 

clean: 
    rm -rf *.hi *.o ffiso *_stub.* 

ghci0: ffiso 
    echo main | ghci FFISo.hs 

ghci1: ffiso 
    echo main | ghci FFISo.hs c.o 

ghci2: ffiso 
    echo main | ghci FFISo.hs c.o FFISo.o 

Biên soạn và liên kết hoạt động tốt:

$ make test 
ghc --make -Wall -O2 -fno-warn-unused-do-bind FFISo.hs c.c -o ffiso 
[1 of 1] Compiling Main    (FFISo.hs, FFISo.o) 
Linking ffiso ... 
./ffiso 
main 
callMeFromHaskell 
callMeFromC 

Tuy nhiên, nếu tôi muốn sử dụng GHCi, nó không thành công với thông điệp này:

$ make ghci0 
echo main | ghci FFISo.hs 
GHCi, version 7.4.1: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Ok, modules loaded: Main. 
Prelude Main> Loading package bytestring-0.9.2.1 ... linking ... done. 
<interactive>: FFISo.o: unknown symbol `callMeFromHaskell' 

Prelude Main> Leaving GHCi. 

Vâng, chúng ta hãy cố gắng đưa ra GHCi các c.o objectfile.

$ make ghci1 
echo main | ghci FFISo.hs c.o 
GHCi, version 7.4.1: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Loading object (static) c.o ... done 
ghc: c.o: unknown symbol `callMeFromC' 
linking extra libraries/objects failed 
make: *** [ghci1] Error 1 
final link ... 

Oh okay ... chúng ta hãy thử với FFISo.o:

$ make ghci2 
echo main | ghci FFISo.hs c.o FFISo.o 
GHCi, version 7.4.1: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Loading object (static) c.o ... done 
Loading object (static) FFISo.o ... done 
ghc: FFISo.o: unknown symbol `bytestringzm0zi9zi2zi1_DataziByteStringziInternal_PS_con_info' 
linking extra libraries/objects failed 
make: *** [ghci2] Error 1 
final link ... 

Và đó là nơi tôi bị mắc kẹt.

Tôi đã thử nghiệm nó với hai môi trường khác nhau với cùng một kết quả:

$ # system 1 
$ ghc --version 
The Glorious Glasgow Haskell Compilation System, version 7.4.1 
$ uname -a 
Linux phenom 3.2.13-1-ARCH #1 SMP PREEMPT Sat Mar 24 09:10:39 CET 2012 x86_64 AMD Phenom(tm) II X6 1055T Processor AuthenticAMD GNU/Linux 

$ # system 2 
$ ghc --version 
The Glorious Glasgow Haskell Compilation System, version 6.12.1 
$ uname -a 
Linux hermann 2.6.32-22-generic-pae #36-Ubuntu SMP Thu Jun 3 23:14:23 UTC 2010 i686 GNU/Linux 

Trả lời

10

Bạn cần phải xác định một số đối tượng hơn để liên kết khi gọi GHCi, vì đối tượng C c.o đặc biệt kén chọn khi nói đến liên kết gọi món.

Trước tiên, bạn cần phải thêm các tệp đối tượng của gói bytestring. Điều này được thực hiện bằng cách thêm -package bytestring vào dòng lệnh GHCi.

Sau đó, bạn cần phải thêm tệp đối tượng thực tế xác định callMeFromC. Khi FFISo.hs được biên dịch, nó không tạo ra tệp đối tượng xuất callMeFromC. Thay vào đó, nó sử dụng quy ước đặt tên GHC và xuất Main_zdfcallMeFromCzuak4_closure, mà thực sự là một biến toàn cục tĩnh trỏ đến phần đóng/"thunk" chứa định nghĩa và môi trường chức năng thực tế. Điều này là để bạn không thể viết một cái gì đó như thế này:

foregin export ccall foo :: IO() 
foo = undefined 

... và có sự sụp đổ runtime ngay sau khi chương trình bắt đầu vì "chức năng giá trị" của foo không thể được đánh giá. Định nghĩa hàm chỉ được kiểm tra khi hàm thực sự được sử dụng.

GHC tạo tệp sơ khai, chứa mã C để gọi hàm Haskell từ C. Tệp này được gọi là FFISo_stub.c và được biên dịch thành FFISo_stub.o cho bạn. Tệp đối tượng này xuất "phiên bản C" của callMeFromC có thể được gọi trực tiếp. Hãy kiểm tra mã được tạo ra, nó khá thú vị.

Tất cả trong tất cả, bạn cần phải sử dụng dòng lệnh này khi gọi GHCi:

ghci -package bytestring FFISo.o c.o FFISo_stub.o FFISo.hs