1

I'm running in to the exact same problem as explained here. Unfortunately, I don't understand what exactly was changed to fix the situation. Did the OP hint to Swift the size of flexible array member to make it visible in Swift? Did the OP modify the C library? I'd like to avoid doing that.

I'm using a C library in Swift that returns a struct with a flexible array member "mbody" at the end.

typedef struct {
    size_t size;
    char mbody[];
} msg

In Swift, when this struct is returned, I have access to the property "size", but not the property "mbody". I've confirmed the mbody is being set properly. A simplified version of the code that initializes the C struct is here:

    const size_t mem_size = msgcount * MSGBOX_SIZE;
    msg* ret = malloc(sizeof(msg) + mem_size);

    if (!ret)
        return NULL;

    ret->size = mem_size;
    memcpy(ret->mbody, msgrecord, MSGBOX_SIZE);

Why is Swift not picking up the size of msg->mbody? Is it a compiler setting? I set it to C99.

2
  • The answer to the question you linked and the comments on it suggest that the chosen solution there was to declare a size for the member array, such that it was no longer a FAM. I'm not convinced that either the OP or the answerer there fully understood what was going on, but it is not at all surprising that changing the FAM to an ordinary array member would resolve the issue. Commented May 18, 2020 at 18:07
  • 1
    I'm not much familiar with Swift, nor especially with its C interop features, but I would not be much surprised if it simply did not provide access to FAMs, at least not by name. I don't see any general-purpose way to do so. Commented May 18, 2020 at 18:13

1 Answer 1

1

I haven't found the proper way to fix this yet, but in case it is never answered, I did find a workaround. I can access the pointer for ret and read the full memory by using ret->size, then advancing off the size of mem_size. I realize there is probably a correct solution out there, and this one still probably has an extra unnecessary step (advance) but I couldn't figure it out yet.

let size = retPointer.pointee.size
let msgRaw = Data(bytes: retPointer, count: (MemoryLayout<msg>.stride + size))
let msgBody = msgRaw.advanced(by: MemoryLayout<Int>.stride) 

In reverse, my workaround is not pretty but works. I'd still appreciate anyone who can show the proper way to handle C flexible array members in structs in Swift. In my workaround, I calculate how many msg objects would need to create a buffer long enough for the body, then copy in to that space.

let totalmem = (mem_size + MemoryLayout<Int>.stride) / MemoryLayout<msg>.stride
let max = Int(ceil(Double(totalmem)) + 1.0)
withUnsafeMutablePointer(to: &msgObj) { orderPtr in
    orderPtr.withMemoryRebound(to: msg.self, capacity: max){ reboundPtr in
        let bodyPtr = UnsafeMutableRawPointer(reboundPtr).advanced(by: MemoryLayout<Int>.stride)
        let bodyBufferPtr = UnsafeMutableRawBufferPointer(start: bodyPtr, count: mem_size)
        body.copyBytes(to: bodyBufferPtr)
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

My suggestion would be to avoid FAMs. They are tricky enough to work with in pure C, and adding another language into the mix seems like asking for an extra helping of trouble. In the particular case presented in the question, I would suggest using an ordinary pointer to the message body, with the size passed around as a separate, accompanying argument. That would be more idiomatic on the C side, and I predict that it will work out better for you.
Thanks for the feedback. I forgot to mention the subsequent call requires a typed pointer which is why I wasn't using a raw pointer from the beginning.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.