. 이유는 GHC API가 가파른 학습 곡선을 가지고 있기 때문입니다.
어쨌든, 내가 말했듯이, 내 의견에서, 이것이 얼마나 깊게 가고 싶은지에 따라 놀라 울 정도로 소수의 FFI 통화가 필요합니다. 아래에서는로드 된 파일에서 표현식을 실행하고 결과를 반환하는 방법에 대한 예제를 제공합니다 (단, show 인스턴스가있는 경우에만 해당). 이것은 기본에 지나지 않으므로 결과는 구조 이어야합니다. 우리는 하스켈 땅을 종료해야하기 때문에
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"
그래서 그래, 그것은 하스켈에서 작동하는 간단한 테스트를 사용하여이를 테스트 할 수 있습니다.
소스가없는 파일에 해당 형식이 정의되어 있기 때문에 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을 리디렉션하려는 것이 좋습니다.
두 개의 중요한 조각을 찾은 것 같습니다. 그러나 그것들을 결합해야합니다! GHC API를 설정하고 사용하는 컴파일 타임 하스켈을 작성하고 C++에서 해당 코드를 FFI를 통해 호출하십시오. 하지만 실제로 해본 적이 없으므로 나는 이것을 실제 답변으로 만드는 것에 자신이 없습니다. –
더 쉬운 해결책이 있었으면 좋겠다. – Heinzi
벙어리 솔루션 : 라이브러리로 사용하는 대신 시스템 호출을 통해 GHC (i)를 원시 코드로 사용할 수있다. –