TLDR:
- Export a haskell function with
foreign export
. - Create a C wrapper that calls
hs_init
when the library is loaded. - Compile the
.hs
and.c
wrapper withghc
, linking withlHSrts-ghc8.6.5
- Use it from C by linking as usual.
(For more details, see here.)
Create a haskell file that exports the function, in this case the haskell
function c_eval
is exported as eval
.
-- This file is Eval.hs.
module Eval() where
import Foreign.C
import Foreign
foreign export ccall "eval" c_eval :: CString -> Ptr CInt -> IO (Ptr CInt)
c_eval s r = do
cs <- peekCAString s
case hs_eval cs of
Nothing -> return nullPtr
Just x -> do
poke r x
return r
hs_eval :: String -> Maybe CInt
hs_eval s = Just (read s)
Create a C wrapper with a constructor and destructor.
/* This file is hsbracket.c. */
#include <HsFFI.h>
static void my_enter(void) __attribute__((constructor));
static void my_enter(void)
{
static char *argv[] = { "libEval.so", 0 }, **argv_ = argv;
static int argc = 1;
hs_init(&argc, &argv_);
}
static void my_exit(void) __attribute__((destructor));
static void my_exit(void)
{
hs_exit();
}
Compile this to get the file libEval.so
. (I use stack
to handle dependencies. If you don't, just do ghc -O2 -dynamic ...
instead)
stack ghc -- -O2 -dynamic -shared -fPIC -o libEval.so Eval.hs hsbracket.c -lHSrts-ghc8.6.5
Now you want to use eval()
in a C program. Note that we need to declare eval
's type.
/* This file is eval.c */
#include <stdio.h>
/* declare eval */
int *eval(char* n, int* d);
int main(int argc, char** argv) {
int d = 0;
if(argc>1) {
/* call eval */
eval(argv[1], &d);
}
printf("d = %d\n", d);
return 0;
}
Compile eval.c
and link it with libEval.so
. Note that gcc
must know where to find the library, so we specify the search path with -L
gcc eval.c -L. -lEval -o eval
or you can compile it with ghc
which i nice because then you can use the Eval_stub.h
for the function prototype:
ghc -- -no-hs-main eval.c -L. -lEval -o eval
To run it, the dynamic linker must also know where to find libEval.so
.
LD_LIBRARY_PATH=. ./eval 543
If this is eval.c
/* This file is eval.c */
#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "Eval_stub.h"
#endif
#include <stdio.h>
int main(int argc, char** argv) {
hs_init(&argc, &argv);
int d = 0;
if(argc>1) {
/* call eval */
eval(argv[1], &d);
}
printf("d = %d\n", d);
hs_exit();
return 0;
}
we build a dynamic binary with
stack ghc -- --make -no-hs-main -optc-O eval.c Eval.hs -o eval
or a static binary with
stack ghc -- --make -no-hs-main -optc-O -optl-static eval.c Eval.hs -o eval