。私がこれをお勧めするのは、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をリダイレクトしたいと思うかもしれません。
私はあなたが2つの重要な部分を見つけたと思うが、それらを組み合わせる必要がある!コンパイル時にGHC APIを設定して使用するHaskellを書き、そのコードをFFI経由でC++から呼び出します。しかし、私は実際にそれをやったことがないので、私はこれを実際の答えにすることに自信がありません。 –
もっと簡単な解決策があることを望みました... – Heinzi
ダム解決策:ライブラリとして使う代わりに、システムコールを使ってGHC(i)をネイティブコードとして使用することができます。 –