2015-08-31 21 views
8

我正在爲C++類編寫一個Haskell包裝器。我決定將它表示爲一個包含指向C++類實例的指針(Foreign.Ptr)的Haskell數據結構。類似的東西。Haskell在對象刪除時調用函數

在C++:

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:

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 

的問題是,我不知道如何正確地執行MyClass的缺失。在某些時候,Haskell垃圾回收器會刪除MyClass對象,但它不會觸發MyClass *內存在C++中釋放。我如何解決這個問題?

我所知道的ForeignPtr,但它使用IO單子,因爲我要包裝的數據結構,以準確表現爲正常的Haskell數據結構,這是不令人滿意的,而不需要明確分配/釋放內存或IO單子。

+3

你的班級副作用是免費的嗎?如果沒有,因爲它在C++中很常見,所以你需要使用'IO' monad,或者_carefully_確保你公開一個純粹的接口。這或者失去了參照透明性,這在Haskell中是一個致命的罪過,因爲它會使程序的行爲幾乎無法預測,尤其是當優化器重寫代碼時。 – chi

+0

@chi是的,這是免費的副作用。 – wrwt

回答

7

「它使用IO單子,因爲我要包裝的數據結構,以準確表現爲正常的Haskell數據結構」

你當然這是不令人滿意的,但不幸的是它不是真的有可能。國外「函數」總是可以做有趣的東西,這在Haskell中是不應該的;類型系統無法查看並阻止它。

這種困境是我們擁有unsafePerformIO的唯一原因(實際上,你的確是你的一個有效應用的好例子)。

我還沒有這樣做我自己還,但你的代碼看起來應該像下面這樣:

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 

我不太清楚那些FunPtr S,希望有人會評論有關的東西那...

+0

我在'ForeignPtr'上想到了使用'unsafePerformIO'這樣的解決方案,但我不確定它能正常工作。如果你是這樣,你能解釋一下這種方法背後發生了什麼嗎? – wrwt

+1

@wrwt每次對'mkMyClass'進行評估(應該只有一次,但不要依賴它)分配一個C++對象,創建一個Haskell值並附帶一個終結器,並將其作爲純值返回。當其中一個這樣的值被垃圾收集時,終結器('cppDeleteMyClass')將會運行。 – chi

+0

關於'FunPtr's。聲明'cppDeleteMyClass'的正確方法是'foreign import ccall'&cpp_delete_MyClass「cppDeleteMyClass :: FunPtr(Ptr() - > IO())'(with&added)。你可能應該修復你的答案。 – wrwt