3

My Code:

getGPS :: String -> IO (Double, Double)
getGPS ip = do
  html <- getHTML ip
  let ztags =Prelude.zip [0..] . filterStr . getTagText . getTags $ html
  let nlat = Prelude.head $ Prelude.map fst . Prelude.filter (\(_, str) -> strEq str ("Latitude:" :: String)) $ ztags
  let nlng = Prelude.head $ Prelude.map fst . Prelude.filter (\(_, str) -> strEq str ("Longitude:" :: String)) $ ztags
  let lat = read (Prelude.head $ Prelude.map snd . Prelude.filter (\(n, _) -> n == nlat + 1) $ ztags) :: Double
  let lng = read (Prelude.head $ Prelude.map snd . Prelude.filter (\(n, _) -> n == nlng + 1) $ ztags) :: Double
  return (lat, lng)

is working fine. Now i wanna export this function via FFI to access it from an C application. I did foreign export ccall getGPS :: CString -> IO (CDouble, CDouble) but this is not working:

GPS.hs:45:1:
    Illegal foreign declaration: requires unregisterised, llvm (-fllvm) or native code generation (-fasm)
    When checking declaration:
      foreign export ccall "getGPS" getGPS
        :: CString -> IO (CDouble, CDouble)

GPS.hs:45:1:
    Unacceptable result type in foreign declaration:
      ‘(CDouble, CDouble)’ cannot be marshalled in a foreign call
    When checking declaration:
      foreign export ccall "getGPS" getGPS
        :: CString -> IO (CDouble, CDouble)

GPS.hs:45:1:
    Couldn't match type ‘Double’ with ‘CDouble’
    Expected type: CString -> IO (CDouble, CDouble)
      Actual type: String -> IO (Double, Double)
    In the expression: getGPS
    When checking declaration:
      foreign export ccall "getGPS" getGPS
        :: CString -> IO (CDouble, CDouble)
Failed, modules loaded: none.

How to export this function correctly?

Martin Fischer
  • 697
  • 1
  • 6
  • 27
  • 1
    Possible duplicate of [Make GHCi load and interpret a module with a "foreign export" declaration (for FFI with C)?](http://stackoverflow.com/questions/28899620/make-ghci-load-and-interpret-a-module-with-a-foreign-export-declaration-for-f) – Yuras Dec 09 '15 at 22:36

1 Answers1

7

there are actually two errors:

  • the type of the foreign export and the getGPS function have to match, so you need a GPS wrapper from CString to CDouble (use peekCString and CDouble to convert them)
  • you can't use a tuple as return argument. There are multiple solutions like using 2 Ptr CDouble arguments or defining a struct.

So a possible solution would be

foreign export ccall "getGPS" getGPS' :: CString -> Ptr CDouble -> Ptr CDouble -> IO ()

getGPS' :: CString -> Ptr CDouble -> Ptr CDouble -> IO ()
getGPS' str d1 d2 = do
  (r1, r2) <- getGPS =<< peekCString str
  poke d1 (CDouble r1)
  poke d2 (CDouble r2)

Remember to call hs_init and hs_exit when using from C Code.

Fabian Schmitthenner
  • 1,696
  • 10
  • 22
  • thanks. but how should i call the function in C? I did: ` double lat, lng; getGPS("79.212.82.103", &lat, &lng); printf("GPS: %d %d\n", lat, lng);` but this is not printing the double-values, it looks like the adress or something like this? – Martin Fischer Dec 10 '15 at 10:55
  • 1
    @MartinFischer you have to use e.g. %f to display a double, with %d it is interpreted as an integer (see e. g. http://www.cplusplus.com/reference/cstdio/printf/ for an overview of flags) – Fabian Schmitthenner Dec 10 '15 at 11:36