AlexanderM
(Alexander Momchilov)
1
Does Swift provide a way to store a C-string ( UnsafePointer<CChar> ) with a static lifetime? Something like a hypothetical:
let s: UnsafePointer<CChar> = "abc"
Where s has a stable pointer, pointing to a constant in the data section of the app's binary, just like a C string constant.
Context
I want to use kSMRightBlessPrivilegedHelper, which is declared as a C macro:
/*!
* @abstract
* The authorization rights key for blessing and installing a privileged helper
* tool.
*/
#define kSMRightBlessPrivilegedHelper \
"com.apple.ServiceManagement.blesshelper"
Since it's just a string literal in C, it has a constant address and you don't have to worry about alloc/freeing it.
But in Swift, it imports as Swift.String , and you have to worry about the lifetime of the scoped pointer you get with
kSMRightBlessPrivilegedHelper.withCString { thisDoesntOutliveTheClosure in
// ...
}
I'd like to have the pointer outlive that closure scope. I'm okay with "leaking it", by having the pointer be allocated for the whole lifetime of the program (it's tiny)
tera
2
smth dirty like this?
private var _helperString: UnsafeMutableRawPointer!
var helperString: UnsafeMutableRawPointer {
if _helperString == nil {
kSMRightBlessPrivilegedHelper.withCString { s in
let len = strlen(s)
_helperString = malloc(len + 1)!
memmove(_helperString, s, len + 1)
}
}
return _helperString!
}
AlexanderM
(Alexander Momchilov)
3
Hah, I had something similar, but extracted into a helper function:
func copyIntoCString(_ str: UnsafePointer<CChar>) -> UnsafePointer<CChar> {
let length = strlen(str)
let result = UnsafeMutablePointer<CChar>.allocate(capacity: length+1)
result.assign(from: str, count: length+1)
return UnsafePointer(result)
}
let kSMRightBlessPrivilegedHelper_pointer = copyIntoCString(kSMRightBlessPrivilegedHelper)
Karl
(👑🦆)
4
It sounds like you might be looking for StaticString. It provides an unscoped .utf8Start pointer.
kSMRightBlessPrivilegedHelper is imported as a String though, not a StaticString.
liuliu
(Liu Liu)
6
If StaticString is not an option, you have to expose this pointer through another C function.
One comment: By being a macro, this line itself rely on linker to dedup all static string at link time. You probably want to modify the C side of things a bit anyway.
eskimo
(Quinn “The Eskimo!”)
7
I had something similar
I just call strdup:
let kSMRightBlessPrivilegedHelper_pointer = strdup(kSMRightBlessPrivilegedHelper)
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple
2 Likes
tera
8
as in another example:
let str = "hello"
fputs(str, stdout)
can't you just pass the "swift" string?
func foo() {
some_C_function(... kSMRightBlessPrivilegedHelper ...)
}
AlexanderM
(Alexander Momchilov)
9
If I'm not mistaken, the pointer you get back from doing that is only guaranteed to be valid for the lifetime of the call of some_C_function. If some_C_function stashes it away somewhere, it'll be dangling.
tera
10
very true, but i think such C api would be rather an exception than a rule and would be documented to have this behaviour, otherwise any code that does anything like this would break:
void foo() {
char s[...];
populate s
some_C_function(s);
}
void bar() {
char* s = malloc(...);
populate s
some_C_function(s);
free(s);
}
AlexanderM
(Alexander Momchilov)
11
One second look, the function I was concerned about doesn't have this problem after all! lol
eskimo
(Quinn “The Eskimo!”)
12
i think such C api would be rather an exception than a rule
It does crop up pretty regularly, often when there’s some complicated set of structures that are set up and passed into a function call. One that I hit commonly is AuthorizationCopyRights.
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple
1 Like