How to define structures that can be passed to C?

Hello,

I would like to invoke a function that I dynamically loaded with dlopen, and I need to pass data to it. Ideally, I could define a structure, and then pass a pointer to the structure to this function, but I have not found a way to express this in Swift.

In C# I can place the [StructLayout(LayoutKind.Sequential)] on a struct, and then the struct becomes blittable and I can pass a pointer to C functions.

The following declaration shows what I am trying to do:

    struct myType {
        var a, b, c: Int32
        var d: Int8
    }
    // This is wrong
    typealias add_wch_def = @convention(c) (UnsafeMutablePointer<myType>) -> CInt

When I compile that, I get:

'(UnsafeMutablePointer<CursesDriver.myType>) -> CInt' (aka '(UnsafeMutablePointer<CursesDriver.myType>) -> Int32') is not representable in Objective-C, so it cannot be used with '@convention(c)'

Any guidance as to what I could try in this scenario?

You can define the type in C, say by putting it your bridging header.

2 Likes

And, in fact, defining it in a C header is the only officially supported way to do this.

4 Likes

This April 2017 quote from @John_McCall suggests tuples can be used for this purpose:

Is this advice this correct?

1 Like

Yes; the layout of a tuple is guaranteed to be the basic in-order rounding-to-alignment layout. We don’t guarantee that primitive type alignments will match C in all cases, but if you have a tuple with no need for alignment padding, it should have the obvious layout.

4 Likes

Everything John said is absolutely correct, but: you should still define the structure in a C header that you import, because that way you have a single definition for the layout, rather than two separate definitions that you need to keep in sync if you make changes. One of Swift's big strengths that doesn't get talked about much is its ability to import C API without futzing with memory layout or calling-convention details. Use the C API directly if you can.

8 Likes

Thanks for all the details, this is super useful.

I found that the C header worked great. Very smooth. Not sure if this will work once I try this on other platforms, but for now I am unblocked.

Thanks!

1 Like

It would be great if we have a way to do this in Swift natively.

1 Like