I have two c files and a c header as follows:
/* C header file: test.h */
#ifndef __TEST_H_
#define __TEST_H_
#define add(X, Y) ((X)+(Y))
#define PI 3.1415926
int sub(int x, int y);
typedef struct person {
char *name;
int age;
} Person;
Person new_person(char *name, int age);
void show_person(const Person *person);
#endif
/* C file 1: sub.c */
#include "test.h"
int sub(int x, int y)
{
return x - y;
}
/* C file 2: person.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"
Person new_person(char *name, int age)
{
Person person = { 0 };
person.age = age;
person.name = calloc(strlen(name) + 1, 1);
strcpy(person.name, name);
return person;
}
void show_person(const Person *person)
{
printf("%s is %d years old.\n", person -> name, person -> age);
}
Then I compile the above c files and merge the compiled object files into a library libtest.a.
Next I try to bind to the above c code in Haskell by using the hsc2hs tool. Well, I am just learning the FFI in Haskell. The .hsc file are as follow:
-- .hsc file: Test.hsc
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CApiFFI #-}
module Test where
import Foreign
import Foreign.C
import System.IO.Unsafe
#include "test.h"
foreign import capi "test.h add"
c_add :: Int -> Int -> Int
foreign import capi "test.h sub"
c_sub :: Int -> Int -> Int
foreign import capi "test.h value PI"
c_pi :: Double
data {-# CTYPE "test.h" "Person" #-} Person =
Person { name :: String, age :: Int} deriving Show
instance Storable Person where
sizeOf _ = #{size Person}
alignment _ = #{alignment Person}
peek ptr = Person <$> (#{peek Person, name} ptr >>= peekCString)
<*> #{peek Person, age} ptr
poke ptr (Person name age) = do
withCString name $ #{poke Person, name} ptr
#{poke Person, age} ptr age
foreign import capi "test.h show_person"
c_show_person :: Ptr Person -> IO ()
showPerson :: Person -> IO ()
showPerson person = alloca $ \ptr -> do
poke ptr person
c_show_person ptr
{- Since FFI doesn't support passing a C strut by value directly,
I create a wrapper function for the new_person C function
-}
#def void wrapper_new_person(Person *person, char *name, int age) \
{ *person = new_person(name, age); }
foreign import capi "Test_hsc.h wrapper_new_person"
c_wrapper_new_person :: Ptr Person -> CString -> Int -> IO ()
newPerson :: String -> Int -> Person
newPerson name age = unsafePerformIO . alloca $ \ptr -> do
name' <- newCString name
c_wrapper_new_person ptr name' age
peek ptr
After that, I run hsc2hs Test.hsc and get three files: Test.hs, Test_hsc.h, Test_hsc.c. Then I compile these code with the following command:
ghc -c Test.hs Test_hsc.c -ltest
The I want to test the foreign call in the ghci, so I run:
ghci Test.o
It tells that unknown symbol 'wrapper_new_person'.
Try again:
ghci Test.o Test_hsc.o -ltest
Now everything seems OK. However when I check the type of newPerson, or c_add, I get the error telling <interactive>:1:1: error: Variable not in scope: newPerson or <interactive>:1:1: error: Variable not in scope: c_add. It seems that the module is not loaded.
I also try to load the Test.hs directly:
ghci Test.hs -ltest -fobject-code
Everything seems OK, but when I run c_sub 3 4 or newPerson "TOM" 35 or showPerson (Person "Tom" 35) or c_add 3 4, ghci tells that unknown symbol 'wrapper_new_person', which is understandable since the Test_hsc.o is not linked.
Occasionly, a fatal error happens telling Access violation in generated code when reading 0x30, Attempting to reconstruct a stack trace...
1. The first question is what happens here?
2. Another question is how to test foreign call in the ghci without Test_hsc.o added into libtest.a?
This question is due to two facts that
- if I add
Test_hsc.ointolibtest.a, then runningghci Test.hs -ltest -fobject-codemakes everything OK. - if I write a
Mainmodule to test it as follows, then everything is OK too.
-- Main.hs
module Main where
import Test
p = newPerson "Tom" 35
main :: IO ()
main = do
showPerson p
putStrLn $ "4 + 5 = " ++ show (c_add 4 5)
putStrLn $ "4 - 5 = " ++ show (c_sub 4 5)
putStrLn $ "PI = " ++ show (c_pi)
Thanks for any information!