fpco / inline-c

284 stars 50 forks source link

Support newtype wrapped ForeignPtr in fptr-ptr marshaller. #57

Closed mboes closed 7 years ago

mboes commented 7 years ago

Foreign pointers are seldom naked. They very often are wrapped in some newtype. We use coerce to pierce through the wrapper.

One could in theory define a custom marshaller in that case, but it's not easy to define a new one by reusing parts of the existing marshaller.

bitonic commented 7 years ago

The general rule in every marshalling operation is that no conversion (even no ops) are performed. For example Doubles must be converted to CDoubles, etc.

This seems an instance of the same debate, but maybe we did not have coerce at the time. I think that if we decide that we do want to use coerce, then we probably want to use it everywhere.

I'm not sure if that'll end up being too confusing for the user though.

mboes commented 7 years ago

Independently of any philosophical debate about the merits of datatype conversions, there is a very practical issue here, which is that ForeignPtr is nearly always wrapped, yet it's difficult to write a custom marshaller specifically for said wrapper. It would have to go something like this:

fooPtrAntiQuoter :: AntiQuoter HaskellIdentifier
fooPtrAntiQuoter = AntiQuoter
  { aqParser = cDeclAqParser
  , aqMarshaller = \purity cTypes cTy cId -> do
      hsTy <- convertType_ "fooCtx" purity cTypes cTy
      hsExp <- getHsVariable "fooCtx" cId
      hsExp' <- [| \cont -> withForeignPtr (unFoo $(return hsExp)) cont |]
      return (hsTy, hsExp')
  }

But, cDeclAqParser isn't exported, and getHsVariable and convertType_ aren't either. And their implementation pulls in a bunch of other functions so copying their code creates a bit of a mess.

A CInt doesn't necessarily have the same representation as an Int, so shying away from having to deal with lossyness in the translation or indeed its cost, is perfectly fair. CChar in fact never has the same range as Char. But the cases that Data.Coerce can handle are special: there is no conversion (no representation change, no copying, in fact it doesn't even exist at runtime).

So I see three possible choices (not mutually exclusive):

bitonic commented 7 years ago

Ah, I see the difference now: you most often can't convert from the wrapped ForeignPtr to a ForeignPtr. I missed this detail at the beginning. Is that so? In that case the "practicality" argument is much more compelling

mboes commented 7 years ago

right.