5

I have an C struct (old library, blah blah blah) which contains an C string, now I need to convert CFString and Swift strings into this c string. Something like

struct Product{
   char name[50];
   char code[20];
}

So I'm trying to assign it as

productName.getCString(&myVarOfStructProduct.name, maxLength: 50, encoding: NSUTF8StringEncoding)

but the compiler is giving me the following error: cannot convert type (int8, int8, int8....) to [CChar].

1
  • 1
    It's funny because I just had the opposite problem. Commented Dec 13, 2014 at 18:39

1 Answer 1

9

A possible solution:

withUnsafeMutablePointer(&myVarOfStructProduct.name) {
    strlcpy(UnsafeMutablePointer($0), productName, UInt(sizeofValue(myVarOfStructProduct.name)))
}

Inside the block, $0 is a (mutable) pointer to the tuple. This pointer is converted to an UnsafeMutablePointer<Int8> as expected by the BSD library function strlcpy().

It also uses the fact that the Swift string productName is automatically to UnsafePointer<UInt8> as explained in String value to UnsafePointer<UInt8> function parameter behavior. As mentioned in the comments in that thread, this is done by creating a temporary UInt8 array (or sequence?). So alternatively you could enumerate the UTF-8 bytes explicitly and put them into the destination:

withUnsafeMutablePointer(&myVarOfStructProduct.name) {
    tuplePtr -> Void in
    var uint8Ptr = UnsafeMutablePointer<UInt8>(tuplePtr)
    let size = sizeofValue(myVarOfStructProduct.name)
    var idx = 0
    if size == 0 { return } // C array has zero length.
    for u in productName.utf8 {
        if idx == size - 1 { break }
        uint8Ptr[idx++] = u
    }
    uint8Ptr[idx] = 0 // NUL-terminate the C string in the array.
}

Yet another possible solution (with an intermediate NSData object):

withUnsafeMutablePointer(&myVarOfStructProduct.name) {
    tuplePtr -> Void in
    let tmp = productName + String(UnicodeScalar(0)) // Add NUL-termination
    let data = tmp.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
    data.getBytes(tuplePtr, length: sizeofValue(myVarOfStructProduct.name))
}

Update for Swift 3:

withUnsafeMutablePointer(to: &myVarOfStructProduct.name) {
    $0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: myVarOfStructProduct.name)) {
        _ = strlcpy($0, productName, MemoryLayout.size(ofValue: myVarOfStructProduct.name))
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.