From: Manuel M T C. <ch...@cs...> - 2005-08-21 12:57:52
|
Axel Simon: > Duncan, Manuel, > > this is a bit of waffle, I tried to reiterate the reasons why things are > the way they are. Here's my (longer) story. > > On Thu, 2005-08-18 at 14:55 +0100, Duncan Coutts wrote: > > I may have replied to this already. Can't quite remember now. Last week > > was all a blur. > > > > On Sun, 2005-08-07 at 22:59 +1000, Manuel M T Chakravarty wrote: > > > > BTW, we've still got another patch that's we probably need before we can > > > > use the mainline c2hs again. However I don't understand it! I'll have to > > > > get Axel to look into that one. :-) It's something in the GenBind > > > > module. As I recall it was something to do with foreign pointers. > > > > > > How big is the change? > > > > I would ask Axel to explain the problem and his solution/patch but he's > > rather busy with other work. > > > > It'll be easeir for me to describe the problem/feature than the > > implemntation. > > > > So apparently newtype's can be passed directly as FFI args. I didn't > > realise that. eg: > > > > {#pointer *cairo_t as Cairo newtype#} > > > > which becomes > > > > newtype Cairo = Cairo (Ptr (Cairo)) > > > > and then for a call we get this decl: > > > > foreign import ccall safe "cairo_create" > > create :: ((Surface) -> (IO (Cairo))) > > > > rather than Ptr Cairo. > > I knew this worked once, but also got a bit confused. The FFI appendix > stopped ForeignPtrs to be marshaled automatically: > > "The argument types ati produced by fatype must be marshallable foreign > types; that is, each ati is either (1) a basic foreign type or (2) a > type synonym or renamed datatype of a marshallable foreign type." > > A "renamed datatype" is a newtype declaration, so these still work. > Before 6.00, ForeignPtrs and newtype-wrapped ForeignPtrs were accepted > as well. Yes, but that had a serious problem for result types, as GHC cannot "guess" which finalizer to attach to a ForeignPtr marshaled from C to Haskell land. > Hence I could have kept the constructors around plain newtype > declarations. However, I wasn't over zealous in stripping these > constructors off. There is a reason for this, see below. > > > Like I said, I didn't realsie that this worked, but it's convenient that > > it does. However it doesn't work for ForeignPtr. Since they need to be > > unwrapped. > > > > The latest c2hs generates with* functions for these: > > > > {#pointer *cairo_t as Cairo foreign newtype#} > > > > newtype Cairo = Cairo (ForeignPtr (Cairo)) > > withCairo (Cairo fptr) = withForeignPtr fptr > > > > So I guess the user is supposed to use this withCairo function to unwrap > > the newtype and the ForeignPtr. > > > > The current version of c2hs in the Gtk2Hs tree takes a different > > approach. This is a rather old patch whihc I think predates the with* > > functions that c2hs builds. > > Yes, I think it does pre-date this with* functions. > > > So it tries to make it more transparent by doing the unwrapping > > automatically at a call site: > > > > So this > > > > buttonPressed :: ButtonClass self => self -> IO () > > buttonPressed self = > > {# call button_pressed #} > > (toButton self) > > > > becomes: > > > > buttonPressed :: ButtonClass self => self -> IO () > > buttonPressed self = > > (\(Button arg1) -> withForeignPtr arg1 $ \argPtr1 ->gtk_button_pressed argPtr1) > > (toButton self) > > > > which is essentially the same as the with* function, but used > > automatically and inlined at the call site. > > It also doesn't pollute the name space. This is more severe that it may > seem at first. In Gtk2Hs we often {#import #} modules, so that c2hs know > about the c2hs type declarations in the module. If we were to switch to > with* functions, we would also have to add these with* functions to the > export list, depending on weather we use the type somewhere else or not. > Furthermore we should then hide all these with* functions in Gtk.hs. > This imposes quite a significant maintenance problem with no apparent > benefit. Yes, that's a good point, but what do you do when a ForeignPtr is returned as a result? What finalizer do you attach? > > Now since we didn't realise that newtypes can be used directly in FFI > > calls the patch also does the newtype unwrapping for non-ForeignPtr > > {#pointer _ newtype#} types too. This can obviously be removed. > > This is where the problems crept up: If you have a newtype wrapping a > ForeignPtr, you need to stip the newtype constructor before you can use > withForeignPtr on it. OTOH, if you have a normal pointer wrapped in a > newtype, you can simply pass the unstripped value to the function. Since > you need the code to strip the constructors for ForeignPtrs, it is > actually easier to do it for simple newtypes around Ptrs as well. At > least emitting the "foreign import" stubs is the same for both, newtype- > wrapped Ptrs and ForeignPtrs. > > The reason why Manuel didn't want to apply my patch is a different one: > I only fixed c2hs half-heartedly. IIRC, our version copes with all > possible newtype wrapped pointers, but not with normal pointers for > which c2hs can generate type synonyms. In GBMonad.hs, I changed > > type PointerMap = FiniteMap (Bool, Ident) (CHSPtrType, String) > > to > > type PointerMap = FiniteMap (Bool, Ident) HsPtrRep > type HsPtrRep = (Bool, CHSPtrType, Maybe String, String) > > . There is a comment explaining the fields of HsPtrRep in > http://cvs.sourceforge.net/viewcvs.py/gtk2hs/gtk2hs/tools/c2hs/gen/GBMonad.hs?rev=1.1&view=markup > > (I can't seem to find the point where this was actually added. We moved > this file from c2hs/gen/ to tools/c2hs/c2hs/gen to tools/c2hs/gen but > there's no trace of this modification.) > > As far as I know, this map is not sufficiently expressive to indicate > that a certain type may actually not newtype-wrapped but just type- > wrapped. That's indeed a problem. I wonder why you implemented this feature in the way you did. Sticking with the above example, why not generate the following? buttonPressed :: ButtonClass self => self -> IO () buttonPressed self = button_pressed_wrapper (toButton self) button_pressed_wrapper (Button arg1) = withForeignPtr arg1 $ \argPtr1 -> gtk_button_pressed argPtr1 The wrapper could go into the bucket for delayed code, in the same way the foreign declaration does. Manuel |