Converting C char array (unsafe pointer) to String - swift

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!

Related

String transfer

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.

Debugging with Address Sanitizer

So I tried running our app with "Address Sanitizer" enabled. And I got this crash:
let sData = "-e5069fba-3612".data(using:String.Encoding.utf8)!
var pointer = sData.withUnsafeBytes {(bytes: UnsafePointer<CChar>) -> UnsafePointer<CChar> in
return bytes
}
pointer = pointer.advanced(by: 1)
let tmpPIN = String(cString: pointer)
print(tmpPIN)
the crash points to let tmpPIN = String(cString: pointer). Does anyone know the reason behind this? I can't figure out why this is happening.
Note, the app runs fine when I disabled the "Address Sanitizer". Should I be worry about this or just ignore it?
It seems you found an answer that works, but I'm adding one because I'm still kind of baffled by such complex code for such a simple problem.
Your code:
Transforms a Swift string to a Data object,
Gets unsafe bytes from that
Does pointer math on the unsafe bytes to move ahead one byte,
Finally converts the result back into a String.
Your fix makes it even more complex by appending an extra byte you don't even want (it works because C strings are expected to have a null character at the end, and your fix adds that).
This could be done far more simply as:
let sData = "-e5069fba-3612"
let tmpPIN = sData2.dropFirst()
The result is exactly the same.
Or you could handle multiple - characters at the start with something like
let tmpPIN = sData.drop { $0 == "-" }
Which gives the same result for this string.
I found this thread... When I add sData.append(0) after I initialize the sData the Address Sanitizer error is gone.

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

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
}

Why we used double and triple pointer in objective-C or C language?

I confused when i want to take single pointer and when should i take double pointer?
In following structure what exactly did?
struct objc_class {
Class isa;
Class super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodLists;
struct objc_cache *cache;
struct objc_protocol_list *protocols;
};
Why we use the methodLists double pointer?
Edited
int sqlite3_get_table(
sqlite3 *db,
const char *zSql,
char ***pazResult,
int *pnRow,
int *pnColumn,
char **pzErrmsg
);
In above scenario what will be meaning of triple pointer char ***pazResult?
Well, in C at least, double-pointers are commonly used for 2D arrays. The most common 2D array is probably an array of C strings (char*'s). Double pointers are also sometimes employed to pass pointers to functions by reference, but this is unlikely to be the use in the code sample you posted.
According to the name methodLists I would guess that this is an array of lists. A (linked) list in C is commonly represented by a pointer to a node, which objc_method_list could be. An array of such lists is then implemented with a double pointer.
It's probably not the case in the code that you referenced, but you also need a double pointer any time you want to pass a pointer to a function and have changes to that pointer be reflected outside the scope of that function.
For example, if you were trying to rewrite the strcpy function so that the user did not have to allocate memory for the source string, you might try something like the following:
void MyStrcpy(char* dst, char* src){
dst = (char*)malloc(sizeof(char)*(strlen(src)+1));
for(int i=0;i<=strlen(src);i++)
dst[i] = src[i];
printf("src: %s ", src);
printf("dst: %s\n\n", dst);
}
If you were then to call that function,
int main() {
char *foo = "foo";
char *newPtr;
MyStrcpy(newPtr, foo);
printf("foo: %s ", foo);
printf("new: %s\n", newPtr);
}
your output would be as follows:
src: foo
dst: foo
foo: foo
new:
You might also get a seg fault when trying to print newPtr, depending your system. The reason for this behavior is the exact same as the reason you wouldn't expect a change to an int that was passed by value to a function to be reflected outside of that function: what you are passing to MyStrcpy is simply the memory address that newPtr references. When you malloc the space for dst inside the function, you are changing the address dst points to. This change will not be reflected outside of the scope of MyStrcpy!
Instead, if you wanted newPtr to point to the new allocated chunk of memory, you need to have dst be a pointer to a pointer, a char **.
void MyStrcpy(char** dst, char* src){
*dst = (char*)malloc(sizeof(char)*(strlen(src)+1));
for(int i=0;i<=strlen(src);i++)
(*dst)[i] = src[i];
printf("src: %s ", src);
printf("dst: %s\n\n", *dst);
}
Now, if you were to call that function:
int main() {
char *foo = "foo";
char *newPtr;
MyStrcpy(&newPtr, foo);
printf("foo: %s ", foo);
printf("new: %s\n", newPtr);
}
You would get your expected output:
src: foo
dst: foo
foo: foo
new: foo
Hope that helps!
See also these questions:
What is double star?
Why does NSError need double indirection? (pointer to a pointer)
In the most general case a double pointer is a pointer to a list of pointers.
In general pointer is used to hold the address of another variable. What if we need to hold the address of pointer ,in that case we use double pointer. When we want to hold the address of double pointer we use triple pointer.