Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No way to create a foreign pointer for input and have it as an out parameter #46

Closed
ian-ross opened this issue Aug 1, 2013 · 4 comments
Labels

Comments

@ian-ross
Copy link
Member

ian-ross commented Aug 1, 2013

Bug imported from C2HS Trac

Trac ticket created: 2012-07-04T10:45:25-0700


I have quite a few C functions like this:

... func(oid *, ...);

oid refers to a non-opaque C struct, and these functions treat it as an out parameter; a C caller would define an instance of the struct (knowing its size) and pass the address of that structure to have it filled in. I'd like to allocate memory for this struct using mallocForeignPtrBytes {# sizeof oid #}, pass the underlying Ptr to the function, and then return the foreign pointer wrapped in an opaque type (defined using {# pointer ... foreign newtype #}). Note that the documentation for mallocForeignPtrBytes specifically says that GHC implements it much more efficiently than a standard ForeignPtr with a finalizer of finalizerFree; otherwise, I could just use malloc and then wrap the pointer on the way out.

As far as I can tell, a {# fun ... #} wrapper cannot implement this pattern. Any output marshaller has to work using the same value passed to the function, which in this case would be a Ptr, not a ForeignPtr. Thus, I can't both create an OID in the input marshaller (using a - so the caller doesn't have to pass one) and return that OID from the function.

At the moment, I've come up with three workarounds, none of which work very well:

  • Write a {# fun ... #} wrapper using withOID* and write a Haskell wrapper function that calls newOID first, requiring an extra five lines for every function using an OID.
  • Give up on mallocForeignPtrBytes and use malloc on input and newForeignPtr on output, with resulting inefficiency (the library uses a lot of these structures).
  • Use alloca on input, and have the output marshaller use mallocForeignPtrBytes and make a copy. Efficient use of memory and efficient reclamation, but an extra copy per call.

Ideally, I'd prefer to have a way to declare this pattern directly in a marshaller. I'd suggest adding a new symbol to apply to the input marshaller, which allows it to provide an output directly.

@ian-ross ian-ross modified the milestone: 0.20.1 Nov 16, 2014
@ian-ross ian-ross modified the milestones: 0.20.1, 0.21.1 Dec 4, 2014
@ian-ross
Copy link
Member Author

ian-ross commented Dec 7, 2014

I don't know who originally created this ticket in the old C2HS Trac, but here's my understanding of what's wanted here. Suppose we have a C header file issue46.h with contents:

typedef struct {
  int a;
  float b;
} oid;

void func(oid *obj, int aval, float bval);

then we'd like for the following C2HS code (the + syntax in the parameter list in the fun hook is new):

module Main where

#include "issue46.h"

{#pointer *oid as Oid foreign newtype#}

{#fun func as ^ {+, `Int', `Float'} -> `Oid'#}

to produce Haskell code like this:

module Main where

newtype Oid = Oid (ForeignPtr Oid)
withOid :: Oid -> (Ptr Oid -> IO b) -> IO b
withOid (Oid fptr) = withForeignPtr fptr

func :: Int -> Float -> IO Oid
func a2 a3 =
  mallocForeignPtrBytes 8 $ \oidfptr ->
  withForeignPtr oidfptr $ \oidptr ->
    let {a2' = fromIntegral a2} in
    let {a3' = realToFrac a3} in
    func'_ oidptr a2' a3' >>
    return $ Oid oidfptr

foreign import ccall safe "Issue46.chs.h func"
  func'_ :: Ptr (Oid) -> CInt -> CFloat -> IO ()

(some imports are missing).

Here, the + as a parameter in the fun hook marks that the argument to the corresponding C function is going to be used to do this sort of fast allocation and return.

Does this seem like a reasonable approach? From what I can tell, it ought to cover the requirements of the original ticket. It would be useful to know which C library the original writer of the ticket was working with, to help get some better ideas of how this might work.

ian-ross added a commit that referenced this issue Dec 7, 2014
@JohnLato
Copy link
Contributor

JohnLato commented Dec 9, 2014

I may have written the original ticket, but I'm not convinced. Doesn't seem like my writing style. But it is something I've wanted before, and I think the proposed solution is pretty reasonable.

@ian-ross
Copy link
Member Author

@JohnLato Thanks for the comment, John. No-one else has offered an opinion, so I think I'll just go ahead and do things the way I'd planned. It seems like it should be a useful capability whoever originally asked for it.

@ian-ross
Copy link
Member Author

ian-ross commented Jan 2, 2015

I've implemented this pretty much as outlined above. It works for a simple test case, but there's a strong chance that it will be possible to break things by some combination of the new "+" parameters and other C2HS features. I'll close the issue for now anyway.

@ian-ross ian-ross closed this as completed Jan 2, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants