我正在为C ++类编写Haskell包装器。我决定将它表示为一个Haskell数据结构,其中包含一个指向C ++类实例的指针(Foreign.Ptr)。这样的事情。
在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对象,但它不会在C ++中触发MyClass *内存释放。我该如何解决这个问题?
我知道ForeignPtr,但是它使用了IO
monad,这是不满意的,因为我希望包装的数据结构与普通的Haskell数据结构完全相同,而不需要用于显式分配/释放内存或IO
monad。
答案 0 :(得分:7)
“它使用
IO
monad,这是不满意的,因为我希望包装数据结构的行为与普通的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
,希望有人会对此发表评论......