FFI ccall struct return by value on the stack?

Suppose I have the following C code: typedef struct Foo { char* p1; /* Some data on the heap. */ size_t s1; /* Size of data. */ char* p2; /* More data on the heap. */ size_t s2; } Foo; /* Allocates and writes two pieces of data on the heap and returns them in a Foo. */ Foo makeFoo(size_t x); Based on my limited understanding of Haskell FFI according to the Haskell 2010 Language Report ( https://www.haskell.org/onlinereport/haskell2010/haskellch8.html), it is not possible to have a ccall for makeFoo() because Foo is not a marshallable foreign result type. Is my understanding correct? However, I believe I can have a ccall if I change makeFoo() to either of the following: Foo* makeFoo(size_t x); void makeFoo(Foo* out, size_t x); The first involves the C code allocating a Foo and returning a pointer to it (so now there's one more pointer for the C code to deallocate later in another function). The second involves the C code writing a Foo value to a piece of memory allocated in Haskell (possibly using Foreign.Marshall.Alloc.alloca). Both signatures work because Foo* is marshallable but are clumsier to use than the original signature. Is there just no way to return a struct by value on the stack? Is there a cleaner way than the above two? Josh

I don't really have an opinion on the two alternatives, they both seem fine to me. I just wanted to chime in on the following:
On 16 Jun 2020, at 16:44, ☂Josh Chia (謝任中)
wrote: Is there just no way to return a struct by value on the stack?
There is indeed no way to do so, and this is a fundamental limitation not of Haskell, but of the ABI on your platform. To call unknown code, you need to know, well, how to call it. On most platforms the only portable/reliable specification for this is the C ABI (which is why every FFIs to/via C), however this ABI just doesn't defined how passing structs as arguments or returning them as results should work (at least not on any platform I'm aware of). This means that *no* language can portably pass structs to/from code via the C ABI (including C code compiled by, say, different compilers!). Kind regards, Merijn

On 16/06/2020 16:44, ☂Josh Chia (謝任中) wrote:
Based on my limited understanding of Haskell FFI according to the Haskell 2010 Language Report (https://www.haskell.org/onlinereport/haskell2010/haskellch8.html), it is not possible to have a ccall for makeFoo() because Foo is not a marshallable foreign result type. Is my understanding correct?
Yes. Structures are sometimes passed into registers (at least on x86-64) depending on their field types. GHC doesn't support this.
However, I believe I can have a ccall if I change makeFoo() to either of the following: Foo* makeFoo(size_t x); void makeFoo(Foo* out, size_t x);
The first involves the C code allocating a Foo and returning a pointer to it (so now there's one more pointer for the C code to deallocate later in another function). The second involves the C code writing a Foo value to a piece of memory allocated in Haskell (possibly using Foreign.Marshall.Alloc.alloca). Both signatures work because Foo* is marshallable but are clumsier to use than the original signature. Is there just no way to return a struct by value on the stack? Is there a cleaner way than the above two?
The second approach seems cleaner to me. With a Storable instance for Foo, it's not that much clumsy. Sylvain

I would return a malloced pointer. After that, it depends on what you want
to do with the Foo.
If you just want to pass it to other C stuff and don't want to read its
contents from Haskell, I would wrap it in an opaque ForeignPtr to be freed
or make sure it gets freed with a `bracket`. Then there's no awkwardness
with generating/updating a Storable instance.
If you need the contents from Haskell, peek it and free it immediately.
Haskell's alloca also allocates internally so it's not like it would be on
the stack in any case.
The important thing is that any bit of code that deals with raw pointers
with no cleanup registered must have async exceptions masked, otherwise if
one is thrown the memory will be leaked.
On Tue, Jun 16, 2020, 16:45 ☂Josh Chia (謝任中)
Suppose I have the following C code:
typedef struct Foo { char* p1; /* Some data on the heap. */ size_t s1; /* Size of data. */ char* p2; /* More data on the heap. */ size_t s2; } Foo;
/* Allocates and writes two pieces of data on the heap and returns them in a Foo. */ Foo makeFoo(size_t x);
Based on my limited understanding of Haskell FFI according to the Haskell 2010 Language Report ( https://www.haskell.org/onlinereport/haskell2010/haskellch8.html), it is not possible to have a ccall for makeFoo() because Foo is not a marshallable foreign result type. Is my understanding correct?
However, I believe I can have a ccall if I change makeFoo() to either of the following: Foo* makeFoo(size_t x); void makeFoo(Foo* out, size_t x);
The first involves the C code allocating a Foo and returning a pointer to it (so now there's one more pointer for the C code to deallocate later in another function). The second involves the C code writing a Foo value to a piece of memory allocated in Haskell (possibly using Foreign.Marshall.Alloc.alloca). Both signatures work because Foo* is marshallable but are clumsier to use than the original signature. Is there just no way to return a struct by value on the stack? Is there a cleaner way than the above two?
Josh _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

I've done this for some code, in both directions. (https://github.com/SimulaVR/godot-haskell/blob/simula/src/Godot/Gdnative/Int... https://github.com/SimulaVR/godot-haskell/blob/simula/src/Godot/Gdnative/Int...) `Foo makeFoo(size_t x)` is internally equivalent (ABI-dependent, but at least on x64 Linux and WIndows) to `Foo* makeFoo(Foo* result, size_t x)`. Alloc the data in Haskell and use that definition. On 2020-06-16 16:44, ☂Josh Chia (謝任中) wrote:
Suppose I have the following C code:
typedef struct Foo { char* p1; /* Some data on the heap. */ size_t s1; /* Size of data. */ char* p2; /* More data on the heap. */ size_t s2; } Foo;
/* Allocates and writes two pieces of data on the heap and returns them in a Foo. */ Foo makeFoo(size_t x);
Based on my limited understanding of Haskell FFI according to the Haskell 2010 Language Report (https://www.haskell.org/onlinereport/haskell2010/haskellch8.html https://www.haskell.org/onlinereport/haskell2010/haskellch8.html), it is not possible to have a ccall for makeFoo() because Foo is not a marshallable foreign result type. Is my understanding correct?
However, I believe I can have a ccall if I change makeFoo() to either of the following: Foo* makeFoo(size_t x); void makeFoo(Foo* out, size_t x);
The first involves the C code allocating a Foo and returning a pointer to it (so now there's one more pointer for the C code to deallocate later in another function). The second involves the C code writing a Foo value to a piece of memory allocated in Haskell (possibly using Foreign.Marshall.Alloc.alloca). Both signatures work because Foo* is marshallable but are clumsier to use than the original signature. Is there just no way to return a struct by value on the stack? Is there a cleaner way than the above two?
Josh
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (5)
-
David Kraeutmann
-
Merijn Verstraaten
-
Patrick Chilton
-
Sylvain Henry
-
☂Josh Chia (謝任中)