Thay vì sử dụng api GHC tôi sẽ đề nghị ràng buộc để Hint cho cách tiếp cận đặc biệt này, mà chỉ là một wrapper đơn giản xung quanh api GHC. Lý do tôi muốn giới thiệu điều này là vì api GHC có một chút đường cong học tập dốc.
Nhưng dù sao, như tôi đã nói Trong bình luận của tôi, tùy thuộc vào mức độ sâu bạn muốn điều này đi nó sẽ đòi hỏi đáng ngạc nhiên vài cuộc gọi FFI. Dưới đây tôi đưa ra một ví dụ về cách chạy các biểu thức từ một tệp đã tải và trả lại kết quả (chỉ khi có một cá thể hiển thị). Đây chỉ là vấn đề cơ bản, trả lại kết quả dưới dạng cấu trúc cũng có thể là.
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
Kể từ khi chúng ta phải thoát khỏi đất Haskell chúng ta phải có một số cách để đề cập đến bối cảnh, Chúng tôi có thể làm điều này với một StablePtr
và tôi chỉ quấn nó trong một IORef
để làm cho nó có thể thay đổi trong trường hợp bạn muốn thay đổi mọi thứ trong tương lai. Lưu ý rằng GHC API không hỗ trợ loại kiểm tra bộ đệm trong bộ nhớ, vì vậy bạn phải lưu mã bạn muốn giải thích vào một tệp tạm thời trước khi tải nó.
-- @@
Chú thích dành cho công cụ Hs2lib của tôi, đừng bận tâm nếu bạn không sử dụng.
tập tin thử nghiệm của tôi là
module Test where
import Control.Monad
import Control.Monad.Instances
-- | This function calculates the value \x->x*x
bar :: Int -> Int
bar = join (*)
và chúng ta có thể kiểm tra điều này bằng cách sử dụng kiểm tra đơn giản
*FFIInterpreter> session <- createContext "Test"
*FFIInterpreter> runExpr session "bar 5"
"25"
Vì vậy, yeah, nó hoạt động trong Haskell, bây giờ để làm cho nó làm việc bên ngoài của Haskell.
Chỉ cần thêm vào đầu tệp một vài hướng dẫn cho Hs2lib về cách soái ModuleName
vì loại đó được xác định trong tệp mà tệp không có nguồn.
{- @@ INSTANCE ModuleName 0 @@ -}
{- @@ HS2HS ModuleName CWString @@ -}
{- @@ IMPORT "Data.IORef" @@ -}
{- @@ IMPORT "Language.Haskell.Interpreter" @@ -}
{- @@ HS2C ModuleName "wchar_t*@4" @@ -}
hoặc
{- @@ HS2C ModuleName "wchar_t*@8" @@ -}
nếu trên một kiến trúc 64bit,
và Just gọi Hs2lib
PS Haskell\FFIInterpreter> hs2lib .\FFIInterpreter.hs -n "HsInterpreter"
Linking main.exe ...
Done.
Và bạn sẽ kết thúc với những người khác, Bao gồm tập tin với
#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
Tôi chưa thử nghiệm mặt C++, nhưng không có lý do nào nó không hoạt động. Đây là một ví dụ rất phù hợp, nếu bạn biên dịch nó thành một lib động, bạn có thể muốn chuyển hướng stdout, stderr và stdin.
Tôi nghĩ bạn đã tìm thấy hai phần quan trọng, nhưng bạn cần phải kết hợp chúng! Viết một số Haskell biên dịch thời gian để thiết lập và sử dụng API GHC và gọi mã đó từ C++ của bạn thông qua FFI. Nhưng tôi chưa bao giờ thực sự làm điều đó, vì vậy tôi không tự tin làm cho câu trả lời này thành một câu trả lời thực sự. –
Tôi đã hy vọng có một giải pháp dễ dàng hơn ... – Heinzi
Giải pháp câm: thay vì sử dụng nó như một thư viện, bạn có thể sử dụng GHC (i) làm mã gốc thông qua các cuộc gọi hệ thống. –