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:)'
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))
}