2011-12-27 9 views
8

私は、haskellコードを解釈して評価する必要のあるC++アプリケーションを作成しています。このコードはコンパイル時にはわかりませんが、ユーザーが指定します。 ライブラリとして、GHCiやHugsのような、haskellコンパイラ/インタープリタを使用する方法はありますか?HaskellインタプリタをC++で書く(ghcまたはhugをライブラリとして使用)

  • 私はFFIを見つけましたが、これはコンパイル時に知られているhaskellコードでしか機能しないようです。
  • 私はGHC APIとヒントを見つけましたが、それらはhaskellからhaskellコードを解釈したいときにのみ動作するように見えます。
+1

私はあなたが2つの重要な部分を見つけたと思うが、それらを組み合わせる必要がある!コンパイル時にGHC APIを設定して使用するHaskellを書き、そのコードをFFI経由でC++から呼び出します。しかし、私は実際にそれをやったことがないので、私はこれを実際の答えにすることに自信がありません。 –

+0

もっと簡単な解決策があることを望みました... – Heinzi

+0

ダム解決策:ライブラリとして使う代わりに、システムコールを使ってGHC(i)をネイティブコードとして使用することができます。 –

答えて

7

。私がこれをお勧めするのは、GHC APIが少し険しい学習曲線を持っているからです。

しかし、とにかく、私が言ったように、あなたがこれをどれくらい深くしたいかによって、驚くほど少ないFFIコールが必要になるでしょう。以下では、ロードされたファイルから式を実行し、結果を返す方法の例を示します(ショーインスタンスがある場合のみ)。これは単なる基本的なものであり、構造体として結果を返しますでも可能です。私たちはHaskellの土地を終了する必要があるので

module FFIInterpreter where 

import Language.Haskell.Interpreter 

import Data.IORef 
import Foreign.StablePtr 

type Session = Interpreter() 
type Context = StablePtr (IORef Session) 

-- @@ Export 
-- | Create a new empty Context to be used when calling any functions inside 
-- this class. 
-- . 
-- String: The path to the module to load or the module name 
createContext :: ModuleName -> IO Context 
createContext name 
    = do let session = newModule name 
     _ <- runInterpreter session 
     liftIO $ newStablePtr =<< newIORef session 

newModule :: ModuleName -> Session 
newModule name = loadModules [name] >> setTopLevelModules [name] 

-- @@ Export 
-- | free a context up 
freeContext :: Context -> IO() 
freeContext = freeStablePtr 

-- @@ Export = evalExpression 
runExpr :: Context -> String -> IO String 
runExpr env input 
    = do env_value <- deRefStablePtr env 
     tcs_value <- readIORef env_value 
     result <- runInterpreter (tcs_value >> eval input) 
     return $ either show id result 

我々はコンテキストを参照するためのいくつかの方法を持っている必要があり、我々はStablePtrでこれを行うことができると私はちょうどあなたがしたい場合には、それは可変にするためにIORefでそれを包みます将来的に物事を変える。 GHC APIはインメモリバッファの型チェックをサポートしていないので、ロードする前に、解釈したいコードを一時ファイルに保存する必要があります。

-- @@注釈は私のツールHs2lib用ですが、使用しない場合は気にしないでください。

私のテストファイルは

module Test where 

import Control.Monad 
import Control.Monad.Instances 

-- | This function calculates the value \x->x*x 
bar :: Int -> Int 
bar = join (*) 

であり、我々はそれがハスケルの外で動作するようになりました

*FFIInterpreter> session <- createContext "Test" 
*FFIInterpreter> runExpr session "bar 5" 
"25" 

だからええ、それはHaskellで動作する簡単なテストを使用して、これをテストすることができます。

ModuleNameをマーシャリングする方法について、Hs2libのいくつかの指示をファイルの先頭に追加するだけです。そのタイプは、ソースがないファイルに定義されているためです。

{- @@ INSTANCE ModuleName 0     @@ -} 
{- @@ HS2HS ModuleName CWString    @@ -} 
{- @@ IMPORT "Data.IORef"     @@ -} 
{- @@ IMPORT "Language.Haskell.Interpreter" @@ -} 
{- @@ HS2C ModuleName "wchar_t*@4"   @@ -} 

または

{- @@ HS2C ModuleName "wchar_t*@8"   @@ -} 

場合は64ビットアーキテクチャ上で、

とだけHs2lib

PS Haskell\FFIInterpreter> hs2lib .\FFIInterpreter.hs -n "HsInterpreter" 
Linking main.exe ... 
Done. 

を起動し、あなたが持つファイルをインクルードし、他の人の中になってしまいます

#ifdef __cplusplus 
extern "C" { 
#endif 
// Runtime control methods 
// HsStart :: IO() 
extern CALLTYPE(void) HsStart (void); 

// HsEnd :: IO() 
extern CALLTYPE(void) HsEnd (void); 

// createContext :: ModuleName -> IO (StablePtr (IORef (Interpreter()))) 
// 
// Create a new empty Context to be used when calling any functionsinside this class. 
// String: The path to the module to load or themodule name 
// 
extern CALLTYPE(void*) createContext (wchar_t* arg1); 

// freeContext :: StablePtr (IORef (Interpreter())) -> IO() 
// 
// free a context up 
// 
extern CALLTYPE(void) freeContext (void* arg1); 

// evalExpression :: StablePtr (IORef (Interpreter())) -> String -> IO String 
extern CALLTYPE(wchar_t*) evalExpression (void* arg1, wchar_t* arg2); 

#ifdef __cplusplus 
} 
#endif 

私はC++側をテストしていませんが、動作しない理由はありません。 これは非常にベアボーンの例です。動的libにコンパイルするとstdout、stderr、およびstdinをリダイレクトしたいと思うかもしれません。

+0

ありがとうございました。残念ながら私はLinux上でこれが必要です。あなたの例をそこに走らせるために私は何ができますか? – Heinzi

+0

この例のHaskellコードはプラットフォームに依存しないので、linuxでも同じことができます。 haskellコードhs2libはLinux上で*はうまく動作する*を生成します。いろいろな理由から、ghcがLinux上で動的なlibsをコンパイルすることはなかった。 http://stackoverflow.com/questions/7652799/compiling-ghc-with-fpic-supportを参照してください。それがあなたに役立つものは、マーシャリングとFFIコードを生成することです。テンポラリファイルを保持するために-Tフラグを渡すだけで、そこから取得できます。そこから静的/共有libにコンパイルするのはあなた次第です。 – Phyx

+1

また、何らかのIPC形式を使用し、ソケットやパイプ経由で通信するhaskell "server"とC++クライアントを持つこともできます。情報をシリアライズするだけです。それはそれにアプローチするもう一つの方法でしょう。しかし、やはり上のHaskellのコードはプラットフォームに依存しません。 – Phyx

4

GHCはHaskellで書かれているので、そのAPIはHaskellから独占的に入手できます。 Daniel Wagner氏が提案したように、Haskellで必要とするインターフェイスを記述し、FFIでCにバインドするのが最も簡単な方法です。これはおそらく、GHC APIをCに直接バインドするよりも簡単です。 Haskellの強みを利用して、必要なインターフェイスを構築し、最上位レイヤのC++でそれらとのインターフェイスを確立するだけです。

ハスケルのFFIはCにのみエクスポートされることに注意してください。その周りにC++ - ishラッパーが必要な場合は、別のレイヤーとして記述する必要があります。

(ところで、抱擁は古代とメンテナンスされていないです。)代わりに、私はちょうどGHC APIの周りに単純化されたラッパーで、この特定のアプローチのためにHintへの結合を示唆しているGHC APIを使用しての

関連する問題