Swift C void* and void** pointers in latest betas (4) - c

In previous betas, I was able to pass a pointer to anything to C doing something like this
var aString : NSString = "This is a string" // create an NSString
var anUnmanaged = Unmanaged.passUnretained(aString) // take an unmanaged pointer
var opaque : COpaquePointer = anUnmanaged.toOpaque() // convert it to a COpaquePointer
var mut : CMutablePointer = &opaque // this is a CMutablePointer
doSomething(mut) // pass the pointer to an helper function written in C
With the C function like this:
void doSomething(void ** ptr)
{
// do something with the pointer, change it and make it point to another nsstring for example
}
Now, (for sure in Beta4, maybe even in Beta3) the syntax translated from C to Swift has changed
Before, the doSomething function was expecting a CMutablePointer (that now has been removed from Swift)
Now, it expects a:
UnsafePointer<UnsafePointer<()>>
() is a typealias to Void
So, syntactically talking is more clear.
However, Now I can't understand how to take the void** pointer of any object like I was doing in previous betas.
Any suggestion?

This worked for me:
var str = "Hello, playground"
var p: UnsafePointer<Void> = unsafeAddressOf(str)

Can you try this:
let strPtr = unsafeBitCast(aString, UnsafePointer<Void>.self)
let strPtrPtr = unsafeBitCast(strPtr, UnsafePointer<Void>.self)
And then:
doSomething(strPtrPtr)

Not sure if this is the correct answer, I hope that someone has a better method to do this.
The main problem was that there is no direct conversion between a Swift Object and a Void UnsafePointer ( UnsafePointer<()> )
However I managed how to do it passing through a COpaquePointer
// since I can't go directly from NSMutableString to UnsafePointer<()> (which is UnsafePointer<Void>) i pass through a COpaquePointer
var aString : NSMutableString = NSMutableString(string: "test") // create an NSString
var anUnmanaged = Unmanaged.passUnretained(aString) // take an unmanaged pointer
var opaque : COpaquePointer = anUnmanaged.toOpaque() // convert it to a COpaquePointer
var anUnsafe : UnsafePointer<()> = UnsafePointer(opaque) // convert it to an UnsafePointer to Void ( () is a Typealias to Void) using the init(_ other: COpaquePointer) initializer
doSomething(&anotherUnsafe)

Related

Converting from Swift string to const char*

I'm trying to pass a const char * to an old C library converted from a Swift string in Swift.
This is the C function I'm calling:
artnet_node artnet_new(const char *ip, int verbose) { ...
how can I convert a Swift string to this const char type? It works when I pass ipAddress like this:
internal var ipAddress = "192.168.1.43"
but dit does not work when I pass it like this
internal var ipAddress:String = "192.168.1.43"
I need this in a function where I need to specify the type:
internal func setupArtnode(ip:String) -> Int{
I tried using AnyObject instead of String but that doesn't work either.
Thanks.
You should be able to pass a String directly to a C function expecting const char * and it will be automatically converted to a null-terminated UTF-8 string:
let string = "string"
let node = artnet_new(string, 1)
See Interacting with C APIs for more information. Here is the relevant excerpt:
When a function is declared as taking an UnsafePointer argument,
it can accept any of the following:
A String value, if Type is Int8 or UInt8. The string will automatically be converted to UTF8 in a buffer, and a pointer to that
buffer is passed to the function.
Not sure why but this code is working. This passes a string to a C function expecting a const char* which seems to be the same as a unsafePointer.
internal func setupArtnode(ipAddress:String) -> NSInteger{
let cString = self.ipAddress.cString(using: String.defaultCStringEncoding)!
let newString:String = NSString(bytes: cString, length: Int(ipAddress.characters.count), encoding:String.Encoding.ascii.rawValue)! as String
let key2Pointer = UnsafePointer<Int8>(newString)
node = artnet_new(key2Pointer, Int32(verbose)) // VERBOSE : true(1) , false(0)
...
Simple way for Swift 3
var ipAddress: String = "192.168.1.43"
var verbose: Int = 1
artnet_node artnet_new((ipAddress as NSString).utf8String, verbose)
You didn't specify what your Swift array contains. In any case, you need to convert your Swift array to an array of Int8:
let str = "Hello world"
let cArray = str.cString(using: .utf8)
artnet_new(cArray, 1)

Cannot convert value of type 'Array[String]' to expected argument type 'Set<String>'

In Swift, I have a function that I am passing an array to, and then using that array in another function. I keep getting this error:
Cannot convert value of type 'Array[String]' to expected argument type 'Set<String>'
#objc func getProductInfo(productIDs: Array<String>) -> Void {
print(productIDs) //this works with correct data
SwiftyStoreKit.retrieveProductsInfo(productIDs) { result in
...
The rest works, and is tested when I pass in a regular array of ["Monthly", "Yearly", "etc..."].
["Monthly", "Yearly", "etc..."] is not an array, it's an array literal. Set can be implicitly initialized with an array literal.
let ayeSet: Set<String> = ["a"] // Compiles
But, it cannot be implicitly initialized with an array.
let bees: Array<String> = ["b"]
let beeSet: Set<String> = bees // Causes Compiler Error
However, if you explicitly initialize it, then it will work.
let sees: Array<String> = ["c"]
let seeSet: Set<String> = Set(sees) // Compiles
So, in your example explicitly initialization should work.
#objc func getProductInfo(productIDs: Array<String>) -> Void {
print(productIDs) //this works with correct data
SwiftyStoreKit.retrieveProductsInfo(Set(productIDs)) { result in
...
You just need to change you method parameter type. SwiftyStoreKit method is expecting a String Set. Your method declaration should be:
func getProductInfo(productIDs: Set<String>)
I've face the issue using the same lib.
This should work
SwiftyStoreKit.retrieveProductsInfo(Set(productIDs))

Swift 3: Compilation error converting String Array to C Char Array

I have the following C struct:
typedef struct {
char** categories;
int category_size;
} category_fmc_s_type;
My Swift array has the following values:
let categories = ["Weekday", "Weekend"]
I want to populate the C Struct field 'categories' with 'Weekday' & 'Weekend'. To do this I call my toPointer():
fileprivate static func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>>.allocate(capacity: args.count)
for (index, value) in args.enumerated() {
buffer[index] = UnsafeMutablePointer<Int8>(mutating: (value as NSString).utf8String!)
}
return buffer
}
I keep getting the following XCode 8 error:
Cannot convert value of type 'UnsafeMutablePointer<UnsafeMutablePointer<Int8>>' to expected argument type 'UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!'
Any suggestions? I don't understand why there is the optional and '!' in the C-Struct definition implicitly.
As the compiler emits as an error, you need to unwrap after Int8 w/ "?" as follows.
fileprivate func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: args.count)
for (index, value) in args.enumerated() {
buffer[index] = UnsafeMutablePointer<Int8>(mutating: (value as NSString).utf8String!)
}
return buffer
}
then,
func testMyCat() {
let categories = ["Weekday", "Weekend"]
let buffer = toPointer(categories)
var mycat = category_fmc_s_type()
mycat.categories = buffer // you would see compile error w/o "?"
}
the code above works w/o error. Martin's solution gives a compile error at
mycat.categories = &cargs (see the link)
I don't know why.
Check the reference of utf8String property of NSString:
Discussion
This C string is a pointer to a structure inside the string object,
which may have a lifetime shorter than the string object and will
certainly not have a longer lifetime. Therefore, you should copy the
C string if it needs to be stored outside of the memory context in
which you use this property.
The term memory context is not well-defined, but one thing sure is that you cannot expect the allocated region for the C string would live forever. When the member categories in the category_fmc_s_type is accessed, the pointers may be pointing to the already freed regions.
Applying the suggestion from Martin R to your code, your code would be like this:
fileprivate static func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: args.count)
buffer.initialize(from: args.lazy.map{strdup($0)})
return buffer
}
And remember, after you finish using the category_fmc_s_type, you need to deallocate the regions allocated by strdup(_:) and UnsafeMutablePointer.allocate(capacity:):
fileprivate static func freePointer(_ pointers: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>, count: Int) {
for i in 0..<count {
free(pointers[i])
}
pointers.deinitialize(count: count)
pointers.deallocate(capacity: count)
}

Storing a reference to array in swift

I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.
Here is what I am trying to accomplish (how the code doesn't work)
class Foo {
var foo : Array<Int>
init(foo: Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5
My findings so far
A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside
B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)
C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).
Are there anything else? Am I missing some Swift construct which allows me to do that?
You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.
You could make use of Swifts (very un-swifty) UnsafeMutablePointer.
Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo, in the sense that they are both just pointers to same address in memory.
class Foo {
var foo : [Int]
var pInner: UnsafeMutablePointer<Int>
init(foo: [Int]) {
pInner = UnsafeMutablePointer(foo)
self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
}
func modify(inout pOuter: UnsafeMutablePointer<Int>) {
foo.append(5) // <-- foo gets new memory adress
pInner = UnsafeMutablePointer(foo)
pOuter = pInner
}
}
var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))
/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)
/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)
Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...
/* When you're done: clear pointers. Usually when using
pointers like these you should take care to .destroy
and .dealloc, but here your pointers are just companions
to an Array property (which has a pointer an reference
counter itself), and the latter will take care of the
objects in memory when it goes out of scope. */
bar.pInner = nil
pOuter = nil
Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.
From the Swift Programming Language,
Structures are always copied when they are passed around in your code, and do not use reference counting.
If you examine the contents of the array variable, you will see that indeed the append works:
class Foo {
var foo : Array
init(_ foo: Array) {
self.foo = foo
}
func modify() {
foo.append(5)
}
func printFoo() {
print("self.foo: \(foo)")
}
}
let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")
produces
self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]
You have taken a copy of a, not a reference to a.
a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. i.e.,
var a = [1,2,3,4]
I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.
class Foo {
var foo : Array<Int>
init(foo: inout Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]

Creating a struct from mach_timebase_info()

Creating a time base info struct in C is easy, but in Swift the following does not work in the playground:
let timebaseInfo: mach_timebase_info_data_t = mach_timebase_info(&timebaseInfo)
The error is Variable used within its own initial value
I understand the error, but I am unable to think of a way to do this without dropping down to C. Is there a Swift only way that I am missing? Any help would be greatly appreciated. :-)
Edit: Actually I see "A" problem with the above, the "=" doesn't make sense. But I did try the following also:
let timebaseInfo: mach_timebase_info_data_t
mach_timebase_info(&timebaseInfo)'
With an error stating timebasedInfo used before initialization. :-(
The mach_timebase_info function is declared as
typealias mach_timebase_info_t = UnsafeMutablePointer<mach_timebase_info>
// ...
func mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t
which means that you can pass an (initialized) mach_timebase_info variable
as an "in-out expression" with &:
var timebaseInfo = mach_timebase_info(numer: 0, denom: 0)
let status = mach_timebase_info(&timebaseInfo)
if status == KERN_SUCCESS {
// ...
}
For more information, see Interacting with C APIs in the "Using Swift with Cocoa and Objective-C" manual:
Mutable Pointers
When a function is declared as taking an UnsafeMutablePointer<Type>
argument, it can accept any of the following:
nil, which is passed as a null pointer
An UnsafeMutablePointer<Type> value
An in-out expression whose operand is a stored lvalue of type Type,
which is passed as the address of the lvalue
An in-out [Type] value, which is passed as a pointer to the start of
the array, and lifetime-extended for the duration of the call
You'll need to use UnsafeMutablePointer<Type>, as follows:
let p = UnsafeMutablePointer<mach_timebase_info_data_t>.alloc(1)
mach_timebase_info(&p.memory)
swift 5.5:
let timebaseInfo: mach_timebase_info_data_t = {
let pointer = UnsafeMutablePointer<mach_timebase_info_data_t>.allocate(capacity: 1)
precondition(mach_timebase_info(pointer) == KERN_SUCCESS)
let info = pointer.pointee
defer { pointer.deallocate() }
return info
}()
func convertToNanoseconds(_ value: UInt64) -> UInt64 {
return (value * UInt64(timebaseInfo.numer)) / UInt64(timebaseInfo.denom)
}

Resources