Home > Net >  Initializing C-struct with const char * in Swift
Initializing C-struct with const char * in Swift

Time:12-15

I have a following C-style struct in the library:

typedef struct ServiceParam
{
    const char *name;
    const char *value;
} ServiceParam;

I'm interested in initializing an array of these structs from Swift, here's what I've tried:

let cHeaders = headers.map{ServiceParam(name: $0.name, value: $0.value)}

But getting the following warning:

Passing 'String' to parameter, but argument 'value' should be a pointer that outlives the call to 'init(name:value:)'

enter image description here

The name and value C-parameters are cast in a form of UnsafePointer<CChar>! and the input type is (name: String, value: String), i.e. Swift-tuple, but I'm flexible with regards to changing the initial type.

So, the whole minimum example looks as follows:

    public func setParams(headers: [(name: String, value: String)] = []) {
        let cHeaders = headers.map{FsKeyServiceParam(name: $0.name, value: $0.value)}
        // Do the work with `cHeaders`
    }

What would be the best way to initialize the aforementioned C-style struct from the Swift call site?

The ServiceParam struct is used temporarily only during the parent function call, but the name and value strings are stored as a C pairs in an array and their lifetime continues after the function returns:

Later on:

        const auto paramPair = std::make_pair(params->name, params->value);
        instance_variable_array.push_back(paramPair);

CodePudding user response:

When passing a Swift string to a C function taking a const char * argument , a temporary C string representation is created automatically. However, that C string is only valid during the function call. That is what happens at

FsKeyServiceParam(name: $0.name, value: $0.value)

and causes the warnings. There is a withCString method which can be used to create a C string representation which is valid in some scope:

name.withCString { cName in
    value.withCString { cValue in
        let sp = ServiceParam(name: cName, value: cValue)
        // Do something with sp ...
    }
}

but again, the C string pointers are not valid after execution has left these scopes.

For a longer lifetime of the C strings you can duplicate the memory and release it later:

let cHeaders = headers.map{ ServiceParam(name: strdup($0.name),
                                         value: strdup($0.value)) }

// Do the work with `cHeaders`

for header in cHeaders {
    free(UnsafeMutablePointer(mutating: header.name))
    free(UnsafeMutablePointer(mutating: header.value))
}
  • Related