2015-05-27 12 views
5

Sto provando a scrivere un programma Haskell che comunica con C (in definitiva per iOS tramite GHC-iOS). Voglio che passi una stringa da C a Haskell, che Haskell lo elabori e poi restituisca alcuni tipi di dati da Haskell a C Structs tramite hsc2s. Non sono riuscito a trovare un tutorial chiaro e semplice. L'unica cosa di cui Haskell ha bisogno da C è String, nient'altro.hsc2hs: Mutate una struttura C con Haskell

Non ho alcun problema con la prima parte, passando una stringa ad Haskell.

testPrint :: CString -> IO() 
testPrint c = do 
    s <- peekCString c 
    putStrLn s 

Per scopi di test e riferimento futuro, voglio solo essere in grado di gestire qualcosa di simile alla seguente.

C struct

struct testdata { 
    char *a; 
    char *b; 
    int c; 
}; 

Haskell Tipo di dati

data TestData = TestData { 
    a :: String, 
    b :: String, 
    c :: Int 
} deriving Show 

-- not sure what the type would look like 
testParse :: CString -> TestData 

ho capito che ho bisogno di typeclass TestData come riponibile e implementare peek, poke, sizeOf e l'allineamento, ma ho bisogno di vedere un semplice esempio prima che io possa davvero capirlo. La maggior parte delle esercitazioni richiede librerie esterne e la rendono più complicata di quanto non debba essere.

Qui ci sono le risorse che ho guardato:

Stackoverflow - How to use hsc2hs to bind to constants, functions and data structures?

Haskell Cafe - FFI for a beginner

Writing Haskell interfaces to C code: hsc2hs

Haskell Wikibook - FFI

Edit: Attualmente dove mi sono bloccato (ottiene l'errore di segmentazione quando setFoo è chiamato in C)

codice Haskell frammento

instance Storable Foo where 
    sizeOf = #{size foo} 
    alignment = alignment (undefined :: CString) 
    poke p foo = do 
    #{poke foo, a} p $ a foo 
    #{poke foo, b} p $ b foo 
    #{poke foo, c} p $ c foo 
    peek p = return Foo 
    `ap` (#{peek foo, a} p) 
    `ap` (#{peek foo, b} p) 
    `ap` (#{peek foo, c} p) 

foreign export ccall "setFoo" setFoo :: Ptr Foo -> IO() 

setFoo :: Ptr Foo -> IO() 
setFoo f = do 
    newA <- newCString "abc" 
    newB <- newCString "def" 
    poke f (Foo newA newB 123) 

C frammento di codice

foo *f; 
f = malloc(sizeof(foo)); 
foo->a = "bbb" 
foo->b = "aaa" 
foo->c = 1 
// this is incorrect 
// setFoo(&f); 
// this is correct 
setFoo(f); 
+0

ho lavorato attraverso il primo e il quarto collegamento. Il problema a cui sto lavorando ora è come esportare una funzione che restituisce 'TestData' a C. Penso che abbia bisogno di assomigliare a' testParse :: CString -> IO (Ptr TestData) 'ma non sono sicuro di come avvolgere TestData in un Ptr. Ho già un'istanza memorizzabile per TestData. – MCH

+0

Ottenuto per la compilazione con 'testParse :: CString -> IO (StablePtr TestData)' ma sto ottenendo un errore di segmentazione in C. Forse è il modo in cui ho impostato Storable. Pubblicherò il codice più tardi. – MCH

+0

hai provato a cercare nel codice sorgente delle associazioni Haskell FFI C esistenti? Sono abbastanza sicuro che non è la prima volta che viene tentata qualcosa del genere. –

risposta

5

Questo è un esempio completo di un programma C che passa una struttura a Haskell Haskell poi muta i valori di quel struct. Non ha dipendenze esterne. Dovresti riuscire a copiare il codice ed eseguirlo se hai GHC. Speriamo che questo serva da esempio semplice e diretto per gli altri.

foo.h

typedef struct { 
    char *a; 
    char *b; 
    int c; 
} foo; 

foo.c

#include "foo.h" 

HsFoo.hsc

{-# LANGUAGE ForeignFunctionInterface #-} 
{-# LANGUAGE CPP      #-} 

module HsFoo where 

import Foreign 
import Foreign.C 

import Control.Applicative 
import Control.Monad 

#include "foo.h" 

data Foo = Foo { 
    a :: CString 
    , b :: CString 
    , c :: Int 
} deriving Show 

instance Storable Foo where 
    sizeOf _ = #{size foo} 
    alignment _ = alignment (undefined :: CString) 

    poke p foo = do 
     #{poke foo, a} p $ a foo 
     #{poke foo, b} p $ b foo 
     #{poke foo, c} p $ c foo 

    peek p = return Foo 
       `ap` (#{peek foo, a} p) 
       `ap` (#{peek foo, b} p) 
       `ap` (#{peek foo, c} p) 

foreign export ccall "free_HaskellPtr" free :: Ptr a -> IO() 
foreign export ccall "setFoo" setFoo :: Ptr Foo -> IO() 
setFoo :: Ptr Foo -> IO() 
setFoo f = do 
    newA <- newCString "abc" 
    newB <- newCString "def" 
    poke f $ Foo newA newB 3 
    return() 

principale.c

#include <stdio.h> 
#include <stdlib.h> 
#include "HsFoo_stub.h" 
#include "foo.h" 

int main(int argc, char *argv[]) { 
    hs_init(&argc, &argv); 

    foo *f; 
    f = malloc(sizeof(foo)); 
    f->a = "Hello"; 
    f->b = "World"; 
    f->c = 55555; 

    printf("foo has been set in C:\n a: %s\n b: %s\n c: %d\n",f->a,f->b,f->c); 

    setFoo(f); 

    printf("foo has been set in Haskell:\n a: %s\n b: %s\n c: %d\n",f->a,f->b,f->c); 

    free_HaskellPtr(f->a); 
    free_HaskellPtr(f->b); 
    free(f); 
    hs_exit(); 
} 

Command Line - compilare i file ed eseguire

$ hsc2hs HsFoo.hsc 
$ ghc -c HsFoo.hs foo.c 
$ ghc -no-hs-main foo.c HsFoo.o main.c -o main 
$ ./main 
+1

Solo per la cronaca: è necessario liberare la memoria allocata da 'malloc' e' newCString' – Yuras

+0

Ho aggiunto malloc per la struct e gratuito per i CStrings. Sembra giusto? – MCH

+1

Sì, sembra buono ora. – Yuras