> On Mar 1, 2017, at 14:23, Kenny Leung via swift-users <swift-users@swift.org> > wrote: > > Hi All. > > Swift automatically bridges String to char * when calling C functions. For > instance, strlen gets translated as: > > public func strlen(_ __s: UnsafePointer<Int8>!) -> UInt > > I can call it from Swift like this: > > strlen("|") > > I’m But, I’m working with a C struct containing a char *: > > public struct _PQprintOpt { > public var header: pqbool /* print output field headings and row > count */ > public var align: pqbool /* fill align the fields */ > public var fieldSep: UnsafeMutablePointer<Int8>! /* field separator */ > ... > } > public typealias PQprintOpt = _PQprintOpt > > When I try to assign to fieldSep like this: > > opt.fieldSep = "|" > > I get the error: > > Cannot assign value of type 'String' to type 'UnsafeMutablePointer<Int8>!' > > I assume that the difference is that strlen declares const char * and > fieldSep is simply char *, so strlen is non-mutable while fieldSep is > mutable. Is this correct? > > I currently have this ugly hack to get this to work: > > var opt :PQprintOpt = PQprintOpt() > guard let fieldSeparator = "|".cString(using: .utf8) else { > throw Errors.databaseConnectionError("Could not set field separator") > } > opt.fieldSep = UnsafeMutablePointer(mutating:fieldSeparator) > > Is there a cleaner way this could work, or should this be considered a > compiler bug?
Hey, Kenny. The const vs non-const part is important, since both the implicit conversion and cString(using:) are allowed to return a pointer to the internal data being used by the String, and modifying that would be breaking the rules (and could potentially cause a crash). In the case of 'fieldSep', it's unlikely that anyone is going to mutate the contents of the string; it was probably just the original author (you?) not bothering to be const-correct. If you control this struct, a better fix would be to use 'const char *' for the field. That said, that's not the main isuse. The implicit conversion from String to UnsafePointer<CChar> is only valid when the string is used as a function argument, because the conversion might need to allocate temporary storage. In that case, it’s important to know when it’s safe to deallocate that storage. For a function call, that’s when the call returns, but for storing into a struct field it’s completely unbounded. So there’s no implicit conversion there. If you’re willing to limit your use of the pointer value to a single block of code, you can use withCString <https://developer.apple.com/reference/swift/string/1538904-withcstring>: myString.withCString { var opt :PQprintOpt = PQprintOpt() opt.fieldSep = UnsafeMutablePointer(mutating: $0) // use 'opt' } Note that it is illegal to persist the pointer value beyond the execution of withCString, because it might point to a temporary buffer. Alternately, if you’re willing to call free() later, you can use the C function strdup: opt.fieldSep = strdup(myString) // use ‘opt’ free(opt.fieldSep) Of course, all of this is overkill for a string literal. Perhaps when Swift gets conditional conformances, we could consider making UnsafePointer<CChar> conform to ExpressibleByStringLiteral. Meanwhile, you can use the type designed specifically for this purpose, StaticString <https://developer.apple.com/reference/swift/staticstring>: let separator: StaticString = “|” opt.fieldSep = UnsafeMutablePointer(mutating: separator.utf8start) Note that you still have to do the init(mutating:) workaround for the fact that you’re not allowed to mutate this string. > > Also, why is the conversion to Swift an IUO? NULL is a totally valid value > for fieldSep. You're welcome to mark that field as _Nullable on the C side, but without annotations Swift makes very few assumptions about pointers imported from C. Hope that helps! Jordan
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users