Swift C interop: Passing Swift Argv to C argv - swift

I am working on a swift wrapper for a C library. One such function in this library expects the command line arguments, in the form of char const *const *. This is linked to swift as Optional<UnsafePointer<UnsafePointer<Int8>?>> From swift I can obtain the command line arguments as CommandLine.unsafeArgv, of type UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>. How can I convert this to the expected immutable type? I know UnsafePointer has a constructor that takes an UnsafeMutablePointer, but I'm unsure of how to handle the nested types. Suggestions on how to correctly convert this?

Try the following (tested with Xcode 12 / swift 5.3)
let values: UnsafePointer<UnsafePointer<Int8>?> =
UnsafeRawPointer(CommandLine.unsafeArgv).bindMemory(
to: UnsafePointer<Int8>?.self,
capacity: Int(CommandLine.argc)
)
your_api_func(Int(CommandLine.argc), values)
Alternate: (on #MartinR comment) tested & worked as well.
CommandLine.unsafeArgv.withMemoryRebound(
to: UnsafePointer<Int8>?.self,
capacity: Int(CommandLine.argc)) { ptr in
your_api_func(Int(CommandLine.argc), ptr)
}

Related

pass a swift var to a C api as void**

I have a C api that looks as follows
struct foo_private_t;
typedef foo_private_t* foo_t;
void foo_func(void**x);
Where the API is intended to be used like this
foo_t x;
void foo_func((void**) &x);
Why the API takes a void** and not a foo_t* is beyond the scope of this question. The problem is when I try to call this API from swift. First I import the C header into swift via the bridging header. Then I try to invoke foo_func with a pointer to a swift object.
var x:foo_t?
foo_func(&x)
// error is cannot convert value of type 'foo_t?' (aka 'Optional<OpaquePointer>') to expected argument type 'UnsafeMutableRawPointer?'
That error is expected, I need a pointer to the pointer, so I tried.
withUnsafeMutablePointer(to: x){ x_p in foo_func(x_p) }
// error is cannot convert value of type 'UnsafeMutablePointer<_>' to expected argument type 'UnsafeMutablePointer<UnsafeMutableRawPointer?>!'
This also seems reasonable as x_p is similar to &x, a single level of pointerness. The next attempt I would have expected to work.
withUnsafeMutablePointer(to: x){ x_p in foo_func(&x_p) }
// error is cannot pass immutable value of type 'UnsafeMutableRawPointer?' as inout argument
Searching around for this error reveals that if I was calling a swift function I should use the inout modifier to a parameter. But since I am calling a C api I am not sure that I can make such a modification. How can I pass a pointer that is a mutable value?
If the intention is to pass the address of x to the C function
in a way that foo_func() can assign a new value to x (which
is what the C code
foo_t x;
void foo_func((void**) &x);
does) then it would be:
var x: foo_t?
withUnsafeMutablePointer(to: &x) {
$0.withMemoryRebound(to: UnsafeMutableRawPointer?.self, capacity: 1) {
foo_func($0)
}
}
Inside withUnsafeMutablePointer(), $0 is a
UnsafeMutablePointer<foo_t?>
and this is rebound to a pointer of the expected type
UnsafeMutablePointer<UnsafeMutableRawPointer?>
I was able to get this to work.
var e = UnsafeMutableRawPointer(x)
foo_func(&e)
x is already a pointer, so it can be converted to a raw pointer. Then I need a pointer to that raw pointer, so I take the address of e. I guess I need the e variable because an implicit temporary value cannot be passed as an argument to an inout parameter. E.g.
foo_func(&UnsafeMutableRawPointer(x))
// error is cannot convert value of type 'foo_t?' (aka 'Optional<OpaquePointer>') to expected argument type 'UnsafeMutableRawPointer?'

Swift 3.2 - compiler doesn't let me use the Darwin.kevent global function

This was working in Swift 3.1, however once I switched to Xcode 9 it stopped compiling. Here's a sample code:
let kq: Int32 = 0
let changelist: UnsafePointer<kevent>! = nil
let nchanges: Int32 = 0
let eventlist: UnsafeMutablePointer<kevent>! = nil
let nevents: Int32 = 0
let timeout: UnsafePointer<timespec>! = nil
Darwin.kevent(kq, changelist, nchanges, eventlist, nevents, timeout)
Error is
error: missing argument labels 'ident:filter:flags:fflags:data:udata:' in call
The problems seems to be caused by the fact that the Darwin module exports both a struct an a function with the same name - kevent, and the compiler doesn't choose the global function and instead wants me to add the struct initializer labels, which doesn't work for me as the parameters lists don't match.
The kevent struct initalizer looks like this
public init(ident: UInt, filter: Int16, flags: UInt16, fflags: UInt32, data: Int, udata: UnsafeMutableRawPointer!)
I even tried declaring all arguments as local variables (as in the sample code), in order to make sure there is no type inference that would make the function call incorrect which could cause the compiler to think that I wanted to use the struct. No luck.
Does anybody know a solution to this "overloading" issue?
I cannot tell you why the fully qualified function name does not compile with Swift 3.2, but
kevent(kq, changelist, nchanges, eventlist, nevents, timeout)
without the Darwin prefix compiles without problems (tested with Xcode 9 GM, Swift 3.2 and Swift 4).
I can confirm that
Darwin.kevent(kq, changelist, nchanges, eventlist, nevents, timeout)
compiles with Swift 3.1 (Xcode 8.3.3), so you might want to file a bug
report.

Pointers in Swift

I'm trying to understand the use of pointers in Swift, in particular: Unsafe[Mutable]Pointer and UnsafeRaw[Mutable]Pointer. I have several questions on the subject.
Is UnsafePointer <T> equal to const T * Pointer in ? and UnsafeMutablePointer <T> is equal to T * Pointer in C?
What is the difference between Unsafe[Mutable]Pointer and UnsafeRaw[Mutable]Pointer?
Why does this compile
func receive(pointer: UnsafePointer<Int> ) {
print("param value is: \(pointer.pointee)")
}
var a: Int = 1
receive(pointer: &a) // prints 1
but this gives me an error?
var a: Int = 1
var pointer: UnsafePointer<Int> = &a // error : Cannot pass immutable value of type 'Int' as inout argument
Is UnsafePointer <T> equal to const T * Pointer in ? and UnsafeMutablePointer <T> is equal to T * Pointer in C?
Well, use a bridging header in a Swift app to see how the C pointers are bridged:
const int *myInt;
int *myOtherInt;
bridges to
var myInt: UnsafePointer<Int32>!
var myOtherInt: UnsafeMutablePointer<Int32>!
What is the difference between Unsafe[Mutable]Pointer and UnsafeRaw[Mutable]Pointer?
Swift 3 added a UnsafeRawPointer API to replace the Unsafe[Mutable]Pointer<Void> type. Conversion between pointers of a different type is no longer allowed in Swift. Instead, the API provides interfaces (.assumingMemoryBound(to:) or .bindMemory(to:capacity:)) to bind memory to a type.
With regard to question 3, the ampersand means that the variable is inout. I don't believe you can declare a variable as inout unless it is being used by a function that directly modifies the underlying memory, but I'll let the experts correct me. Instead, use withUnsafePointer.
Thanks to Martin's helpful comment, this syntax was never valid in Swift, and there is no safe way to create "free pointers" to Swift variables.

Convert String to UnsafeMutablePointer<char_t> in Swift

I'm working with a third party c API I'm trying to call one of the functions with a simple string. Something like this:
some_c_func("aString");
I get a build error:
Type 'UnsafeMutablePointer<char_t>' does not conform to protocol 'StringLiteralConvertible'
I've seen some suggestions to use utf8 on String or similar conversions, which gets nearly there, but with the following error:
some_c_func("aString".cStringUsingEncoding(NSUTF8StringEncoding));
'UnsafePointer<Int8>' is not convertible to 'UnsafeMutablePointer<char_t>'
How can I create an UnsafeMutablePointer?
It all depends on what char_t is.
If char_t converts to Int8 then the following will work.
if let cString = str.cStringUsingEncoding(NSUTF8StringEncoding) {
some_c_func(strdup(cString))
}
This can be collapsed to
some_c_func(strdup(str.cStringUsingEncoding(NSUTF8StringEncoding)!))
WARNING! This second method will cause a crash if func cStringUsingEncoding(_:) returns nil.
Updating for Swift 3, and to fix memory leak
If the C string is only needed in a local scope, then no strdup() is needed.
guard let cString = str.cString(using: .utf8) else {
return
}
some_c_func(cString)
cString will have the same memory lifecycle as str (well similar at least).
If the C string needs to live outside the local scope, then you will need a copy. That copy will need to be freed.
guard let interimString = str.cString(using: .utf8), let cString = strdup(interimString) else {
return
}
some_c_func(cString)
//…
free(cString)
it may be simpler than that - many C APIs pass strings around as char * types, and swift treats these as unsafe.
try updating the C API (good) or hack it's header files (bad) to declare these as const char * instead.
in my experience this allows you to pass standard swift String types directly to the C API.
apparently a constant is required, in order to conform to the protocol.
I haven't tried passing strings like that, but I have a C function that I call from Swift, that takes a lot more parameters than shown here, among which is a reference to a Swift C typecast buffer to hold an error string. The compiler doesn't complain and the function call works. Hopefully this will steer you closer to the answer and you can provide an update with the final answer or someone else can.
var err = [CChar](count: 256, repeatedValue: 0)
var rv = somefunc((UnsafeMutablePointer<Int8>)(err))
if (rv < 0) {
println("Error \(err)")
return
}

Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString

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
Swift 1.1 (or perhaps earlier) has even better C string bridging:
let haystack = "This is a simple string"
let needle = "simple"
let result = String.fromCString(strstr(haystack, needle))
The CString type is gone completely.
The String struct in Swift has an init routine you can use like:
let myString = String(cString: myUnsafePointer)
see also init(cString: UnsafePointer)