Converting from Swift string to const char* - c

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)

Related

How do I pass a multiline string from Python to C using ctypes?

I have the following simple C function:
void pyentry(const char *config)
{
printf("%s\n",config);
fflush(stdout);
}
My ctypes definition is as follows:
libc = ct.CDLL("./amr.so")
entry = wrap_function(libc, 'pyentry', ct.POINTER(Checkpoint), [ct.c_wchar_p])
json = "this is a test"
start = entry(json)
Where wrap_function is simply a wrapper for more easily defining ctypes access to C functions:
def wrap_function(lib, funcname, restype, argtypes):
func = lib.__getattr__(funcname)
func.restype = restype
func.argtypes = argtypes
return func
I have compiled as a shared library and I am trying to call it, but in C it is only printing the first character of the string I send in. I am assuming this is because I have the wrong argument tpyes in my ctypes definition, but I am not having any luck figuring out out the right one.
Can someone tell me why my C function is only seeing the first character in the passed string?
Try:
entry = wrap_function(libc, 'pyentry', None, [ct.POINTER(ct.c_char)])
json = "this is a test".encode('utf-8')
pyentry takes const char* and returns void. So argtypes and restype could be [ct.POINTER(ct.c_char)] and None.
And char* points a sequence of bytes, not Python string. So json should be converted to bytes.

Swift: Turning a String into UnsafeMutablePointer<Int8>

I have a C function mapped to Swift defined as
predictX010(inputString: UnsafePointer<emxArray_char_T>!, ypred: UnsafeMutablePointer<emxArray_real_T>!)
I want to input a string in the inputString, but in order to do that I have to play around with emxArray_char_T which is
emxArray_char_T.init(data: UnsafeMutablePointer<Int8>!, size: UnsafeMutablePointer<Int32>!, allocatedSize: Int32, numDimensions: Int32, canFreeData: boolean_T)
My string will consist of let x = ":1580222503,GCP001,007,Male,30,Left,1,IL8 and IL10,0000; 0,281411,-78,521074,-3,344657,132,347776,-93,25,44" and I just cannot figure out how to input it in data of the emxArray_char_T
First you should write a wrapper function in your bridging header that accepts char pointers then cast/create the emxArray_char_T in c IE:
// casting the func as void because you didn't specify
void predictWrapper(const char *aChar, char *bChar) {
// do your casting and call original func predictX010(...)
}
Then in swift (This isn't going to be pretty)
var arg: String = "some arg"
var arg2: String = "another arg"
// use closure to prevent dangling pointer
arg.withCString{ body in // body is UnsafePointer<Int8>
arg2.withCString{ body2 in // we'll cast this to UnsafeMutablePointer
var arg2Mutable = UnsafeMutablePointer<Int8>(mutating: body2)
//call your wrapper
predictWrapper(body, arg2Mutable)
}
}
You may be able to use the original types and function, but i've always found it easier (less banging my head on the desk) to use the most standard c types you can in swift and casting to custom/complex types in c

Using C in Swift - strlcpy - Cannot convert value of type 'UnsafeMutablePointer<_>'

I am trying to use strlcpy in Swift 3.0, but keep getting the error "Cannot convert value of type 'UnsafeMutablePointer<_>' to expected argument type 'UnsafeMutablePointer!'"
Here is my code
func login(portal: String, username: String, password: String) {
var loginEvent = VidyoClientInEventLogIn()
let portalCArray = UnsafeMutablePointer<Int8>(mutating: (portal as NSString).utf8String!)
withUnsafeMutablePointer(to: &loginEvent.portalUri) {
strlcpy($0, portalCArray, MemoryLayout.size(ofValue: $0))
}
}
where VidyoClientInEventLogIn is:
typedef struct VidyoClientInEventLogIn_
{
/*! Portal URI, i.e. "https://example.test.com" */
char portalUri[URI_LEN];
} VidyoClientInEventLogIn;
C arrays are imported to Swift as tuples. But the memory layout of
C structures is preserved in Swift, therefore you can use the address
of the first tuple element loginEvent.portalUri.0 (which has type CChar aka Int8)
as the target address.
Also you can pass a Swift String directly as argument to a function
taking a UnsafePointer<CChar> parameter, a temporary C string
representation is created automatically.
This simplifies things to:
func login(portal: String, username: String, password: String) {
var loginEvent = VidyoClientInEventLogIn()
strlcpy(&loginEvent.portalUri.0, portal, MemoryLayout.size(ofValue: loginEvent.portalUri))
}

What makes Swift think that [String] is Int?

I can write some short and valid example of code:
var array = ["one","two"];
var name = array[0];
All is ok. No errors. name now contains one.
Now suppose I write:
var array = ["one","two"];
var name = array["0"];
It will be a error, and its clear why: array is not a dictionary and I should access it contents by index.
But why does error say that "Type Int does not conform to protocol ExtendedGraphemeClusterLiteralConvertible?
Why Int, if it is [String] ?
By the way, if I produce an error in another way
var array = ["one":0,"two":1];
var name = array[0];
It is clear: "type DictionaryIndex<String, Int> does not conform to protocol IntegerLiteralConvertible"
That error message is because you are supplying a string literal, not an instance of the String type, where the compiler is expecting an Int (since that's what Array's subscript method accepts). The way the compiler would convert a string literal to an instance of type Int is to use a method in the StringLiteralConvertible or ExtendedGraphemeClusterLiteralConvertible protocols, so it checks to see if the Int type conforms to one of those. Since Int doesn't conform, you get the error message you're seeing.
This explains Daniel T's additional information:
var array = ["one","two"]
array["0"] // trying to convert string literal to Int
var foo: Int = "0" // trying to convert string literal to Int
var index = "0"
array[index] // trying to convert String instance to Int
Likewise, your final example shows the compiler attempting the same thing—trying to convert an integer literal to an instance of DictionaryIndex<String, Int>, because a Dictionary instance's subscript can be passed either an instance of that dictionary's Key type or a DictionaryIndex<Key, Value>.
subscript of Array declared as:
struct Array<T> : MutableCollectionType, Sliceable {
//...
subscript (index: Int) -> T
It expect Int as index. then if you do:
array["0"]
It's wrong, because "0" is not Int. But, in other words, if "0" can be Int, it will be OK.
Here, "0" is what in Swift? it's string, but actually it is ExtendedGraphemeClusterLiteral. Swift has 2 string literal types, ExtendedGraphemeClusterLiteral and StringLiteral.
"" empty → StringLiteral
"A" just one character → ExtendedGraphemeClusterLiteral
"AB" two or more characters → StringLiteral
Anyway, try this on Playground:
extension Int: ExtendedGraphemeClusterLiteralConvertible {
typealias ExtendedGraphemeClusterLiteralType = String
public init(unicodeScalarLiteral value: String) {
self = value.toInt() ?? 0
}
public init(extendedGraphemeClusterLiteral value: String) {
self = value.toInt() ?? 0
}
}
var array = ["one","two"];
var name = array["0"]; // -> "one"
"Type 'Int' does not conform to protocol 'ExtendedGraphemeClusterLiteralConvertible'" means...
compiler: "If Int conforms to ExtendedGraphemeClusterLiteralConvertible, I can compile this!"
Interestingly, if you do this:
var array = ["one","two"];
var index = "0"
var name = array[index];
The error will be 'String' is not convertible to 'Int'.
I think this happens because the system attempts to convert "0" into an Int which means doing a lot of protocol checks. Array's index type is convertible to Self.Index which is a ForwardIndexType... Well, things get weird.
Whereas Swift has strong rules against implicit variable type conversion. So if you use a variable as an index, it is more clear.
Note that var foo: Int = "0" produces the same error as your array["0"].

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

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)

Resources