String transfer - swift

I'd like to return a String back to Swift using this code:
MyFile.h:
+ (char *) myCoolCode;
MyFile.mm:
+(string)myCoolCode {
string myCoolString = "";
myCoolString += "123";
return myCoolString;
}
MyFile.swift:
let superCoolString = MyBridge.myCoolCode()
print(superCoolString)
But obviously it doesn't seems working the right way because it's crashing somewhere deep inside.

As others have already pointed out in the comments you should fix the return type of your .mm file to char * and not string. You should always keep those two types the same. An example of your function implementation can be:
- (char *)myCoolCode
{
char str[] = "foobar";
//You can do whatever you want with str here. Just make sure it's null terminated
return str;
}
Then in your swift code:
let myCoolString = String(cString: MyBridge.myCoolCode())
print(myCoolString)
Reference on the string constructor is here.
The reason your code was crashing is probably because you were returning an instance of std::string which doesn't really work in Swift. You can use std::string but you have to convert it to char * when returning it. You can do so as is shown here.

Related

Swift reading char (not char *) as C string pointer

I just started with with Swift this week, specifically Swift 4, and I'm using a C library through a bridging header, liblo, which handles sending/receiving OSC (Open Sound Control) formatted messages over a network socket.
I'm able to start a server thread, receive OSC messages via C callback->Swift closure, and read the numeric argument values with Swift just fine. I'm running into trouble, however, with reading string values.
The liblo message argument type lo_arg is a C typedef for a union and the string argument types are declared as simple chars which are mapped to Swift as Int8.
In C, you can grab the string via &argv[i]->s from your callback's lo_arg **argv array. In an Obj-C project with liblo, I use:
// get string value of first argument
lo_arg arg = argv[0];
NSString *s = [NSString stringWithUTF8String:&arg->s];
// do something with s
In Swift, I've tried getting the address of the Int8 and feeding it to String which works, but only grabs the first character:
// get string value of first argument
if var arg : lo_arg = argv?.pointee![0] {
withUnsafePointer(to: &arg.s) {
let s = String(cString: $0)
// so something with s
}
}
Am I doing something wrong? I would think these would be equivalent but passing $0 to strlen() ala print("strlen: \(strlen($0)") only prints a length of "1". I've verified a multi-character string is indeed being sent with a non-Swift test program. I'm wondering now if Swift is somehow assuming the string is a single character instead of C string head address and/or I need some further pointer conversion.
After some digging, I can confirm Swift truncates the lo_arg->s & lo_arg->S string values to 8 bytes on my 64 bit system aka sizeof(char). This happens when trying to read the string from an lo_arg coming from Swift. Reading the same value in C works fine, so Swift seems to reserve/allow reading from only the space for a single char. Forwarding the lo_arg from Swift to C and printing the string via printf() also shows truncated strings up to 8 characters.
The quick fix is to avoid reading the strings from the lo_arg generated by Swift, grab an lo_arg from the raw lo_message in C, and cast the char "pointer" to a const char* Swift will understand as a variable length string. Here are some working utility functions I added to my bridging header:
/// return an lo_message argv[i]->s in a format Swift can understand as a String
const char* lo_message_get_string(lo_message message, int at) {
return (const char *)&lo_message_get_argv(message)[at]->s;
}
/// return an lo_message argv[i]->S in a format Swift can understand as a String
const char* lo_message_get_symbol(lo_message message, int at) {
return (const char *)&lo_message_get_argv(message)[at]->S;
}
In Swift, I can then convert to a String:
let s = String(cString: lo_message_get_string(msg, 0))
// do something with s

Weird issues when passing an array of structs from Swift to C

I'm passing an array of structs from Swift to a C function. The struct looks like this:
struct Struct {
int a;
float b;
float c;
const char* d;
const char* e;
const char* f;
const char* g;
int h[4];
};
Function signature of the C function:
void test(struct Struct* structs);
Weirdly, when I print d in the C function, it's often something different than what I set it to in the Swift code: usually an empty string or some garbage. When I set d to a very long string, it works correctly. The other strings are passed correctly too. Is that some struct alignment issue?
As #MartinR suggested, when I pass the string to the struct's constructor, Swift creates a temporary C char array on the stack, copies the string's data into it and passes its pointer to the constructor. Immediately after that, the char array is no longer valid. Here's an example code demonstrating this:
let s = Struct(string: "string")
print(s.string) // Prints "string"
print("lol") // Overwrite the stack
print(s.string) // Prints "lol"
See https://stackoverflow.com/a/40121697/243225 for possible solutions.

Converting C char array (unsafe pointer) to String

I have an UnsafeMutablePointer<Character> filled by a CoreFoundation method.
If I NSLog it with %s placeholder, it outputs just fine.
But if I try with Swift's print it just writes the memory address.
Tried nearly everything... also I don't understand why if I try to access the underlying memory property I get a EXC_BAD_ACCESS.
let deviceName = UnsafeMutablePointer<Character>.alloc(64)
/* other statements in which deviceName is filled */
NSLog("device %s by %s", deviceName, manufacturerName)
// Outputs correctly the string
print(String(deviceName[0]))
// Get an EXC_BAD_ACCESS error at runtime
print(String(deviceName.memory))
// Get an EXC_BAD_ACCESS error at runtime
let str = withUnsafePointer(&deviceName) { String.fromCString(UnsafePointer($0)) }
print(str)
// Outputs an empty string
print("\(deviceName) by \(manufacturerName)")
// Outputs just memory addresses
You seem unwilling to show your code, so I can't really help. But this looks just wrong:
let deviceName = UnsafeMutablePointer<Character>.alloc(64)
/* other statements in which deviceName is filled */
That is not how to get hold of a C string in Swift, and if you believe that it is a C string in Swift, you're wrong; it would need to be an array of Int8 (C characters), not Swift Character (a struct!), to be a C string.
In other words, C char (your question's title) is not Swift Character - they are nothing like one another. A C char is a small number, a Swift Character is an object in an object-oriented language that C knows nothing about!

Swift convert string to UnsafeMutablePointer<Int8>

I have a C function mapped to Swift defined as:
func swe_set_eph_path(path: UnsafeMutablePointer<Int8>) -> Void
I am trying to pass a path to the function and have tried:
var path = [Int8](count: 1024, repeatedValue: 0);
for i in 0...NSBundle.mainBundle().bundlePath.lengthOfBytesUsingEncoding(NSUTF16StringEncoding)-1
{
var range = i..<i+1
path[i] = String.toInt(NSBundle.mainBundle().bundlePath[range])
}
println("\(path)")
swe_set_ephe_path(&path)
but on the path[i] line I get the error:
'subscript' is unavailable: cannot subscript String with a range of
Int
swe_set_ephe_path(NSBundle.mainBundle().bundlePath)
nor
swe_set_ephe_path(&NSBundle.mainBundle().bundlePath)
don't work either
Besides not working, I feel there has got to be a better, less convoluted way of doing this. Previous answers on StackOverflow using CString don't seem to work anymore. Any suggestions?
Previous answers on StackOverflow using CString don't seem to work anymore
Nevertheless, UnsafePointer<Int8> is a C string. If your context absolutely requires an UnsafeMutablePointer, just coerce, like this:
let s = NSBundle.mainBundle().bundlePath
let cs = (s as NSString).UTF8String
var buffer = UnsafeMutablePointer<Int8>(cs)
swe_set_ephe_path(buffer)
Of course I don't have your swe_set_ephe_path, but it works fine in my testing when it is stubbed like this:
func swe_set_ephe_path(path: UnsafeMutablePointer<Int8>) {
println(String.fromCString(path))
}
In current version of Swift language you can do it like this (other answers are outdated):
let path = Bundle.main.bundlePath
let param = UnsafeMutablePointer<Int8>(mutating: (path as NSString).utf8String)
It’s actually extremely irritating of the library you’re using that it requires (in the C declaration) a char * path rather than const char * path. (this is assuming the function doesn’t mutate the input string – if it does, you’re in a whole different situation).
If it didn’t, the function would come over to Swift as:
// note, UnsafePointer not UnsafeMutablePointer
func swe_set_eph_path(path: UnsafePointer<Int8>) -> Void
and you could then rely on Swift’s implicit conversion:
let str = "blah"
swe_set_eph_path(str) // Swift implicitly converts Strings
// to const C strings when calling C funcs
But you can do an unsafe conversion quite easily, in combination with the withCString function:
str.withCString { cstr in
swe_set_eph_path(UnsafeMutablePointer(cstr))
}
I had a static library (someLibrary.a) written in C++ compiled for iOS.
The header file (someLibrary.h) had a function exposed like this:
extern long someFunction(char* aString);
The declaration in Swift looks like this:
Int someFunction(aString: UnsafeMutablePointer<Int8>)
I made an extension to String:
extension String {
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer((self as NSString).UTF8String)
}
}
So then I can call the method like so:
someFunction(mySwiftString.UTF8CString)
Update: Make String extension (swift 5.7)
extension String {
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
}

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
}