Swift call C function with char array / string parameter

Hi, community,

I'm trying call C function in the task() function in SwiftUI.

  1. The code runs without Darwin, why Darwin is not needed

  2. in the strncpy() call, UnsafeMutablePointer can be used to convert C array, shouldn't it be the one with Buffer: UnsafeMutableBufferPointer

Thanks

import SwiftUI
//import Foundation
//import Darwin

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!").padding()
        }.task{await task()}
    }
    
    func task() async {
        let str = "hello"
        let LEN = 1024
        var buf = [CChar](repeating: 0, count: LEN)
        strncpy(UnsafeMutablePointer<CChar>(&buf), str, LEN - 1)
        fputs(buf, stdout)
    }
}

Because you're importing SwiftUI, which implicitly imports Foundation, which implicitly imports Darwin.

This line in your code is not correct (and the compiler should have given you a warning that you're creating a dangling pointer).

The expression UnsafeMutablePointer<CChar>(&buf) creates a pointer that only remains valid for the duration of the initializer call and becomes invalid before strncpy is called.

You should use one of the withUnsafe… variants, which create a pointer whose lifetime is guaranteed for the duration of the closure you pass to the method. Like this:

buf.withUnsafeMutableBufferPointer { bufferPointer in
  strncpy(bufferPointer.baseAddress!, str, LEN - 1)
}

You may also want to take a look at Array.init(unsafeUninitializedCapacity:initializingWith:).

fwiw, this works as is:

let str = "hello"
fputs(str, stdout)

the relevant magic happens automatically.

Thanks Ole.

I reread this post:
https://forums.swift.org/t/read-text-file-line-by-line/28852/6

Can you please check my new snippet. It seems that I don't need to put one of those Unsafe*Pointer there at all, I don't understand the purpose of those Unsafe*Pointer.

//import SwiftUI
func task() async {
    let str = "hello"
    let LEN = 1024
    var buf = [CChar](repeating: 0, count: LEN)
    //strncpy(&buf, str, LEN - 1) // this works
    memcpy(&buf, str, LEN - 1) // this works too
    fputs(buf, stdout)
}

Can you please check my new snippet.

Please have a read of The Peril of the Ampersand. I think that’ll explain the background to this issue and give you a general understanding of when you need to call the withUnsafeXXX routines. If you still have questions after that, post a follow up here and we’ll take a look.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

4 Likes

Thanks Quinn, that is a great post of yours.

Can you please show me some or write another great tutorial on Swift and C interoperability. The C pointers, array, malloc, etc.. will be involved in the C code side.

Can you please show me some or write another great tutorial on Swift
and C interoperability. The C pointers, array, malloc, etc.. will be
involved in the C code side.

For general info on how to work with C APIs, see Imported C and Objective-C APIs.

If you have follow-up questions, please post the details here.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

Terms of Service

Privacy Policy

Cookie Policy