Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a function at object deletion in Haskell

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.

like image 706
DLunin Avatar asked Aug 31 '15 14:08

DLunin


1 Answers

“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 FunPtrs, hopefully somebody will comment something about that...

like image 149
leftaroundabout Avatar answered Nov 09 '22 10:11

leftaroundabout