Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a C string inside a struct to a Swift String

I am using an existing C library in a Swift app and trying to convert a C character buffer to a Swift String.

Bridging.h

typedef struct { char mfg[8]; char model[8]; } motorcycle;

void GetMotorcycle(motorcycle *m);

Example.swift

var cycle = motorcycle(mfg: (0,0,0,0,0,0,0,0), model: (0,0,0,0,0,0,0,0));
GetMotorcycle(&cycle)
var manufacturer : String = String.fromCString(cycle.mfg)     // Error

This produces "Could not find a overload for 'fromCString' that accepts the supplied arguments"

Since Swift treats the C character array as a tuple, I cannot find a way to convert it to a Swift String.

like image 650
Dave Avatar asked Oct 20 '25 02:10

Dave


2 Answers

Well, at least that C character array is only eight characters long, since there's currently no way to iterate over a tuple. Here's how you can convert it:

func char8ToString(tuple: (CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar)) -> String {
    let arr: unichar[] = [unichar(tuple.0), unichar(tuple.1),
                        unichar(tuple.2), unichar(tuple.3),
                        unichar(tuple.4), unichar(tuple.5),
                        unichar(tuple.6), unichar(tuple.7)]
    let len = arr.reduce(0) { $1 != 0 ? $0 + 1 : $0 }
    return NSString(characters: arr, length: len)
}

var manufacturer: String = char8ToString(cycle.mfg)

Hopefully we get language support for a better way to handle this case soon!

like image 170
Nate Cook Avatar answered Oct 21 '25 15:10

Nate Cook


Swift 5.2 — this solution can accepts arbitrary-length tuples, doesn’t do any extra copying, and uses the latest conventions for safe memory binding.

extension String {
    init<T>(tupleOfCChars: T, length: Int = Int.max) {
        self = withUnsafePointer(to: tupleOfCChars) {
            let lengthOfTuple = MemoryLayout<T>.size / MemoryLayout<CChar>.size
            return $0.withMemoryRebound(to: UInt8.self, capacity: lengthOfTuple) {
                String(bytes: UnsafeBufferPointer(start: $0, count: Swift.min(length, lengthOfTuple)), encoding: .utf8)!
            }
        }
    }
}
like image 39
Wil Shipley Avatar answered Oct 21 '25 16:10

Wil Shipley