2015-05-27 19 views
5

C ile iletişim kuran bir Haskell programı yazmaya çalışıyorum (en sonunda iOS ile GHC-iOS üzerinden). C'den Haskell'e bir dizgeyi iletmesini istiyorum, Haskell'i işlemek ve sonra Haskell'den C Structs'a bazı Data tiplerini hsc2s ile döndürmek istiyorum. Net, basit bir öğretici bulmakta başarısız oldum. Haskell'in C'den ihtiyacı olan tek şey, String, başka bir şey değil.hsc2hs: Haskell ile bir C yapısını değiştir

İlk kısımda bir sorunum yok, Haskell'e bir ip geçiriyorum.

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

Test amacıyla ve ileride başvurmak için, sadece aşağıdaki gibi bir şeyle başa çıkmak istiyorum.

C Planı

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

Haskell Veri Türü

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

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

Ben göz depolanabilir olarak TestData typeclass ve uygulamak gerektiğini anlamanızı dürter, sizeof ve hizalama, ama basit görmeliyim önce gerçekten anlayabiliyorum. Öğreticilerin çoğu, dış kütüphaneleri gerektirir ve olması gerekenden daha karmaşık hale getirir.

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

Düzenleme:

İşte baktım kaynaklardır Şu anda takılıp nerede (segmentasyon hatası olduğunda alır setFoo C olarak adlandırılır

Haskell Kod Parçacığı

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ı Kod Parçacığı

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

çalıştırın. Şu anda çalışmakta olduğum problem, 'TestData' 'yı C'ye döndüren bir fonksiyonun nasıl dışa aktarılacağıdır. Bence bu' testParse :: CString -> IO (Ptr TestData) 'gibi görünmesi gerekiyor ama nasıl emin olacağımı bilmiyorum. TestData bir Ptr içine sarın. TestData için bir Storable örneğim var. – MCH

+0

'testParse :: CString -> IO (StablePtr TestData)' ile derlemek için var ama C içinde segmentasyon hatası alıyorum. Belki de Storable kurulum yoludur. Kodu daha sonra gönderirim. – MCH

+0

varolan Haskell FFI C bağlarının kaynak kodunu incelemeyi denediniz mi? Bunun gibi bir şey ilk kez denemediğinden eminim. –

cevap

5

Bu Haskell bu yapı değerlerini mutasyon sonra Haskell bir yapı geçen bir C programının tam bir örneğidir. Dış bağımlılıkları yoktur. Kodu kopyalayıp GHC'niz varsa çalıştırmanız gerekir. Umarım bu diğerleri için basit, basit bir örnek olarak hizmet edecektir.

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() 

anac

#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(); 
} 

Komut Satırı - dosyaları derlemek ve ben birinci ve dördüncü bağlantı üzerinden çalışıyoruz

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

Sadece kayıt için: 'malloc' ve 'newCString' – Yuras

+0

tarafından ayrılmış belleği boşaltmanız gerekir Yapı için malloc ekledim ve CStrings için ücretsiz. Bu doğru görünüyor mu? – MCH

+1

Evet, şimdi iyi görünüyor. – Yuras