I'm writing a Haskell wrapper for a C++ class. I decided to represent it as a Haskell Data structure containing a pointer (Foreign.Ptr) to the class instance in C++. Something like that.
In 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();
}
In 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
The problem is, I don't know how to correctly implement MyClass deletion. At some point, Haskell garbage collector will delete MyClass object, but it won't trigger MyClass* memory freeing in C++. How do I fix that?
I'm aware of ForeignPtr, but it uses IO
monad, which is not satisfying because I want the wrapped data structure to behave exactly as a normal Haskell data structure, without the need for explicit allocating/freeing memory or IO
monads.
“it uses
IO
monad, which is not satisfying because I want the wrapped data structure to behave exactly as a normal Haskell data structure”
Sure you do, but unfortunately it's not really possible. Foreign “functions” can always do funny stuff that shouldn't be possible in Haskell; the type system has no way to look there and prevent it.
That dilemma is the only (!) reason we have unsafePerformIO
, and indeed yours is a good example of a valid application for that thing.
I haven't done this myself yet, but your code should look something like the following:
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
I'm not quite sure about those FunPtr
s, hopefully somebody will comment something about that...