7

HaskellのFFIとGHCのインタラクティブモードに関して問題があります。

(ソースは要点からも入手できます):

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 ()

cc:

#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 $@

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

コンパイルとリンクは正常に機能します。

$ 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

ただし、GHCiを使用したい場合は、次のメッセージで失敗します。

$ 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.

さて、GHCiにオブジェクトファイルを与えてみましょうc.o

$ 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 ... 

オーケー...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 ... 

そして、それは私が立ち往生しているところです。

2つの異なる環境でテストしたところ、同じ結果になりました。

$ # 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
4

1 に答える 1

10

GHCi を呼び出すときに、リンクするオブジェクトをさらに指定する必要があります。これは、C オブジェクトc.oはリンク順序に関して特にうるさいためです。

bytestringまず、パッケージのオブジェクト ファイルを追加する必要があります。これは-package bytestring、GHCi コマンドラインに追加することによって行われます。

次に、 を定義する実際のオブジェクト ファイルを追加する必要がありますcallMeFromC。をコンパイルするFFISo.hsと、エクスポートするオブジェクト ファイルは生成されませんcallMeFromC。代わりに GHC 命名規則と exports を使用しますMain_zdfcallMeFromCzuak4_closure。これは実際には、実際の関数定義と環境を含むクロージャー/「サンク」を指す静的グローバル変数です。これは、次のような記述ができないようにするためです。

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

...そして、「関数値」をfoo評価できないため、プログラムが開始するとすぐにランタイムがクラッシュします。関数定義は、関数が実際に使用された後にのみ検査されます。

GHC は C から Haskell 関数を呼び出すための C コードを含むスタブ ファイルを生成します。このファイルは と呼ばれFFISo_stub.c、 にコンパイルさFFISo_stub.oれます。callMeFromCこのオブジェクト ファイルは、直接呼び出すことができる「C バージョン」をエクスポートします。生成されたコードを自由に調べてください。非常に興味深いものです。

全体として、GHCi を呼び出すときは、次のコマンド ラインを使用する必要があります。

ghci -package bytestring FFISo.o c.o FFISo_stub.o FFISo.hs
于 2012-04-12T12:56:17.467 に答える