2015-08-31 25 views
8

Bir C++ sınıfı için bir Haskell sarıcı yazıyorum. C++ sınıf örneğine bir işaretçi (Foreign.Ptr) içeren bir Haskell Veri yapısı olarak temsil etmeye karar verdim. Bunun gibi bir şey. C++Haskell'de nesne silme işleminde bir işlev çağırma

:

class MyClass { 
public: 
    double my_method(); 

    // ... 
}; 

extern "C" MyClass* cpp_new_MyClass() { 
    return new MyClass(); 
} 

extern "C" double cpp_my_method(MyClass *obj) { 
    return obj->my_method(); 
} 

Haskell'de:

Data MyClass = MyClass (Ptr()) 

foreign import ccall "cpp_new_MyClass" cppNewMyClass :: Ptr() 
foreign import ccall "cpp_my_method" cppMyMethod :: Ptr() -> Double 

mkMyClass :: MyClass 
mkMyClass = MyClass cppNewMyClass 

myMethod :: MyClass -> Double 
myMethod (MyClass ptr) = cppMyMethod ptr 

sorun şu ki, doğru Sınıfım silinmesini nasıl uygulanacağı bilmiyorum. Bir noktada, Haskell çöp toplayıcı MyClass nesnesini silecektir, ancak C++'da MyClass * belleğini serbest bırakmayacaktır. Bunu nasıl düzeltirim?

Ben ForeignPtr farkında değilim, ama hafıza veya IO monads azat/açık tahsisi için gerek kalmadan, ben sarılmış veri yapısı normal Haskell veri yapısı tam olarak davranmasını istedikleri için tatmin edici değildir IO monad kullanır .

+3

Sınıfınızın yan etkisi ücretsiz mi? Değilse, C++ 'da yaygın olduğu gibi,' IO 'monadını kullanmak için _have_ ya da _carve_ saf bir arayüze sahip olduğunuzdan emin olun. Haskell'deki ölümcül bir günah olan referanssal şeffaflığı kaybetmek ya da kaybetmek, özellikle programın davranışını tahmin etmek neredeyse imkansız hale geleceğinden, özellikle de en iyi duruma getirici kodunuzu yeniden yazarken. – chi

+0

@chi Evet, bu yan etki ücretsizdir. – wrwt

cevap

7

“ ben sarılmış veri yapısı normal Haskell veri yapısı ”

yapmanız Tabii tam olarak davranmasını istedikleri için tatmin edici değildir IO monad kullanır, ama ne yazık ki bu gerçekten mümkün değildir. Yabancı “ fonksiyonları ” her zaman Haskell'de mümkün olmamak kaydıyla eğlenceli şeyler yapabilir; Tip sistemi oraya bakmak ve onu önlemek için hiçbir yolu yoktur.

Bu ikilem, bizim (0) unsafePerformIO nedenimizdir ve gerçekten sizin için geçerli bir uygulamanın iyi bir örneğidir.

henüz kendim yapmadım ama kod gibi görünmelidir şunlardır:

extern "C" void cpp_delete_MyClass(MyClass* obj) { 
    delete obj; 
} 
foreign import ccall "cpp_new_MyClass" cppNewMyClass :: IO (Ptr()) 
foreign import ccall "&cpp_delete_MyClass" cppDeleteMyClass :: FunPtr (Ptr() -> IO()) 

data MyClass = MyClass (ForeignPtr()) 

mkMyClass :: MyClass 
mkMyClass = unsafePerformIO $ do 
    newObj <- cppNewMyClass 
    fPtr <- newForeignPtr cppDeleteMyClass newObj 
    return $ MyClass fptr 

O FunPtr s hakkında tam emin değilim, umarım birileri hakkında bir şeyler yorum yapacak Bu ...

+0

'ForeignPtr'de' unsafePerformIO' kullanarak böyle bir çözüm düşündüm, ancak düzgün çalışacağından emin değilim. Eğer öyleyseniz, bu yaklaşımdaki sahnelerin arkasında neler olduğunu açıklayabilir misiniz? – wrwt

+1

@wrwt 'mkMyClass' her zaman değerlendirildiğinde (yalnızca bir kez olmalı, ancak buna güvenmeyin), bir C++ nesnesi tahsis edilir, bir Haskell değeri buna eklenmiş bir finalizer ile oluşturulur ve salt bir değer olarak döndürülür. Bu değerlerden biri çöp toplandığında, finalizer (cppDeleteMyClass') çalıştırılacaktır. – chi

+0

'FunPtr's hakkında. CppDeleteMyClass'ı ifade etmenin doğru yolu 'yabancı ccall 've cpp_delete_MyClass' cppDeleteMyClass :: FunPtr (Ptr() -> IO())' dır (& eklenir). Muhtemelen cevabını düzeltmelisin. – wrwt