While playing with Standard C Library functions in Swift, I came across problems when passing C strings around. As a simple example (just to demonstrate the problem), the Standard C Library function
char * strdup(const char *s1);
is exposed to Swift as
func strdup(_: CString) -> UnsafePointer<CChar>
which means that the return value of strdup()
cannot be passed to another strdup()
call:
let s1 : CString = "abc"
let s2 = strdup(s1) // OK, s2 is a UnsafePointer<CChar>
let s3 = strdup(s2) // error: could not find an overload for '__conversion' that accepts the supplied arguments
My question is: How to create a Swift CString
from a UnsafePointer<CChar>
,
so that the C string returned by one standard library function can be passed to another function?
The only way that I could find is (using code from How do you convert a String to a CString in the Swift Language?):
let s2a = String.fromCString(s2).bridgeToObjectiveC().UTF8String
let s3 = strdup(s2a)
But I do not find this satisfying for two reasons:
- It is too complicated for a simple task.
- (Main reason:) The above conversions works only if the C string is a valid UTF-8 string, otherwise it fails with a runtime exception. But a C string is an arbitrary sequence of characters, delimited by a NUL character.
Remarks/Background: Of course, high-level functions using high-level data structures like Swift String
or Objective-C NSString
are preferable. But there are BSD functions in the
Standard C Library which do not have an exact counterpart in the Foundation frameworks.
I came across this problem while trying to answer Accessing temp directory in Swift.
Here, mkdtemp()
is a BSD function for which no exact NSFileManager
replacement exists
(as far as I know).
mkdtemp()
returns a UnsafePointer<CChar>
which has to be passed to the
NSFileManager
function stringWithFileSystemRepresentation
which takes a CString
argument.
Update: As of Xcode 6 beta 6, this problem does not exist anymore because the mapping of C-Strings into Swift has been simplified. You can just write
let s1 = "abc" // String
let s2 = strdup(s1) // UnsafeMutablePointer<Int8>
let s3 = strdup(s2) // UnsafeMutablePointer<Int8>
let s4 = String.fromCString(s3) // String