Swift String from imported unsigned char 2D array - ios

I am using a 3rd party C library in my iOS application, which I am in the process of converting from Objective-C to Swift. I hit an obstacle when attempting to read one of the structs returned by the C library in Swift.
The struct looks similar to this:
typedef unsigned int LibUint;
typedef unsigned char LibUint8;
typedef struct RequestConfiguration_ {
LibUint8 names[30][128];
LibUint numberNames;
LibUint currentName;
} RequestConfiguration;
Which is imported into Swift as a Tuple containing 30 Tuples of 128 LibUint8 values. After a long time of trial and error using nested withUnsafePointer calls, I eventually began searching for solutions to iterating a Tuple in Swift.
What I ended up using is the following functions:
/**
* Perform iterator on every children of the type using reflection
*/
func iterateChildren<T>(reflectable: T, #noescape iterator: (String?, Any) -> Void) {
let mirror = Mirror(reflecting: reflectable)
for i in mirror.children {
iterator(i.label, i.value)
}
}
/**
* Returns a String containing the characters within the Tuple
*/
func libUint8TupleToString<T>(tuple: T) -> String {
var result = [CChar]()
let mirror = Mirror(reflecting: tuple)
for child in mirror.children {
let char = CChar(child.value as! LibUint8)
result.append(char)
// Null reached, skip the rest.
if char == 0 {
break;
}
}
// Always null terminate; faster than checking if last is null.
result.append(CChar(0))
return String.fromCString(result) ?? ""
}
/**
* Returns an array of Strings by decoding characters within the Tuple
*/
func libUint8StringsInTuple<T>(tuple: T, length: Int = 0) -> [String] {
var idx = 0
var strings = [String]()
iterateChildren(tuple) { (label, value) in
guard length > 0 && idx < length else { return }
let str = libUint8TupleToString(value)
strings.append(str)
idx++
}
return strings
}
Usage
func handleConfiguration(config: RequestConfiguration) {
// Declaration types are added for clarity
let names: [String] = libUint8StringsInTuple(config.names, config.numberNames)
let currentName: String = names[config.currentName]
}
My solution uses reflection to iterate the first Tuple, and reflection to iterate the second, because I was getting incorrect strings when using withUnsafePointer for the nested Tuples, which I assume is due to signage. Surely there must be a way to read the C strings in the array, using an UnsafePointer alike withUsafePointer(&struct.cstring) { String.fromCString(UnsafePointer($0)) }.
To be clear, I'm looking for the fastest way to read these C strings in Swift, even if that involves using Reflection.

Here is a possible solution:
func handleConfiguration(var config: RequestConfiguration) {
let numStrings = Int(config.numberNames)
let lenStrings = sizeofValue(config.names.0)
let names = (0 ..< numStrings).map { idx in
withUnsafePointer(&config.names) {
String.fromCString(UnsafePointer<CChar>($0) + idx * lenStrings) ?? ""
}
}
let currentName = names[Int(config.currentName)]
print(names, currentName)
}
It uses the fact that
LibUint8 names[30][128];
are 30*128 contiguous bytes in memory. withUnsafePointer(&config.names)
calls the closure with $0 as a pointer to the start of that
memory location, and
UnsafePointer<CChar>($0) + idx * lenStrings
is a pointer to the start of the idx-th subarray. The above code requires
that each subarray contains a NUL-terminated UTF-8 string.

The solution suggested by Martin R looks good to me and, as far as I can see from my limited testing, does work. However, as Martin pointed out, it requires that the strings be NUL-terminated UTF-8. Here are two more possible approaches. These follow the principle of handling the complexity of C data structures in C instead of dealing with it in Swift. Which of these approaches you choose depends on what specifically you are doing with RequestConfiguration in your app. If you are not comfortable programming in C, then a pure Swift approach, like the one suggested by Martin, might be a better choice.
For the purposes of this discussion, we will assume that the 3rd party C library has the following function for retrieving RequestConfiguration:
const RequestConfiguration * getConfig();
Approach 1: Make the RequestConfiguration object available to your Swift code, but extract names from it using the following C helper function:
const unsigned char * getNameFromConfig(const RequestConfiguration * rc, unsigned int nameIdx)
{
return rc->names[nameIdx];
}
Both this function's signature and the RequestConfiguration type must be available to the Swift code via the bridging header. You can then do something like this in Swift:
var cfg : UnsafePointer<RequestConfiguration> = getConfig()
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig(cfg, cfg.memory.currentName)))
{
print(s)
}
This approach is nice if you need the RequestConfiguration object available to Swift in order to check the number of names in multiple places, for example.
Approach 2: You just need to be able to get the name at a given position. In this case the RequestConfiguration type does not even need to be visible to Swift. You can write a helper C function like this:
const unsigned char * getNameFromConfig1(unsigned int idx)
{
const RequestConfiguration * p = getConfig();
return p->names[idx];
}
and use it in Swift as follows:
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig1(2)))
{
print(s)
}
This will print the name at position 2 (counting from 0). Of course, with this approach you might also want to have C helpers that return the count of names as well as the current name index.
Again, with these 2 approaches it is assumed the strings are NUL-terminated UTF-8. There are other approaches possible, these are just examples.
Also please note that the above assumes that you access RequestConfiguration as read-only. If you also want to modify it and make the changes visible to the 3rd party library C code, then it's a different ballgame.

Related

Calling C function from Swift shows different results in CLion than in Xcode [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 9 months ago.
Improve this question
I am trying to call a C function from Swift , but I do not know exactly how to define variables to pass parameters. This is the function declaration:
/* Function Declarations */
extern void compute_feature_set(const double input[11025],
double M_data[], int M_size[2],
double D_data[], int D_size[2],
double DD_data[],
int DD_size[2],
double VT[10], double *mp,
double r_42[42], double *fM);
The data is an array of floats. So I tried ​​:
let s = data.compactMap{ Double($0)}
var mSize = Array<Int32>(repeating:Int32(0.0), count:2)
var dSize = Array<Int32>(repeating:Int32(0.0), count:2)
var dD_Size = Array<Int32>(repeating:Int32(0.0), count:2)
var mData = Array<Double>(repeating:0.0, count:48)
var dData = Array<Double>(repeating:0.0, count:48)
var dD_Data = Array<Double>(repeating:0.0, count:48)
var vt = Array<Double>(repeating:0.0, count:10)
var mp = Double(0.0)
var r = Array<Double>(repeating:0.0, count:42)
var fM = Double(0)
compute_feature_set(s, &cout, &mSize, &vx, &dSize, &dD_Data, &dD_Size, &vcta, &mp, &r, &fM)
When I run the code in Clion with the following function it works fine and the output matches the expected values:
static void main_compute_feature_set(void)
{
static double dv[11025];
double DD_data[48];
double D_data[48];
double M_data[48];
double r_42[42];
double VT[10];
double fM;
double mp;
int DD_size[2];
int D_size[2];
int M_size[2];
/* Initialize function 'compute_feature_set' input arguments. */
/* Initialize function input argument 'input'. */
/* Call the entry-point 'compute_feature_set'. */
argInit_11025x1_real_T(dv);
compute_feature_set(dv, M_data, M_size, D_data, D_size,
DD_data, Dd_size, VT,
&mp, r_42, &fM);
}
However, when I run my implementation in Swift, I get very different results.
You could try passing pointers of the Arrays, rather than the Arrays directly.
Using Imported C Functions in Swift | Apple Developer Documentation
Call Functions with Pointer Parameters
Whenever possible, Swift avoids giving you direct access to pointers. When importing C function parameters, however, Swift maps pointer parameters to standard library pointer types.
The following tables use Type as a placeholder type name to indicate syntax for the mappings.
For return types, variables, and arguments, the following mappings apply:
C Syntax
Swift Syntax
const Type *
UnsafePointer<Type>
Type *
UnsafeMutablePointer<Type>
double[] is pretty much equivalent to double * in this case.
Looks like the problem with your code is passing data to your function. You use compactMap to make an Array of Double and then pass the pointer of this array. But Array and Double are struct in Swift so you pass the pointer of struct with structs instead of array of double values.
To convert your data to array of bytes you should use withUnsafeBytes e.g.:
Swift:
let data = Data([0xaa, 0xbb, 0xcc, 0xdd])
data.withUnsafeBytes {
passData($0)
}
C/ObjC:
void passData(const double input[11025]) {
NSLog(#"%x", input[0]); // Prints: ddccbbaa
}

Why does the raw parts not correspond to either endianness?

I'm trying to optimize as much as possible an operation done on slices of u32 from arrays of u8. As such, I'm testing different options (for loops, iterators, using ByteOrder crate, etc.)
As part of these tests, I also wanted to check out if I could improve it using from_raw_parts standard function.
Here is my code:
use byteorder::{ByteOrder, BigEndian, LittleEndian};
use std::io::Read;
fn main(){
let random_bytes = (0..4).map(|_| { rand::random::<u8>() }).collect::<Vec<u8>>();
let random_bytes = random_bytes.as_slice();
let view = &random_bytes as *const _ as *const u32;
let slice: &[u32] = unsafe { std::slice::from_raw_parts(view, 1) };
println!("{:x?}", slice);
println!("{:x?}", LittleEndian::read_u32(&random_bytes[0..4]));
println!("{:x?}", BigEndian::read_u32(&random_bytes[0..4]));
println!("{:x?}", &random_bytes[0..4]);
}
I would've expected at least one one of the two Little or Big endian to be equal to the first print, but instead this does not seem to be the case, e.g. an example output
[d951db30]
143600ff
ff003614
[ff, 0, 36, 14]
What am I doing wrong?
The problem is here:
let view = &random_bytes as *const _ as *const u32;
A slice is a 2-machine-word struct that contains the pointer to data and the element count as members.
By doing &random_bytes, you are taking a reference to this slice structure (which itself contains a pointer and length), not acquiring the underlying pointer.
Slices have an as_ptr method that returns the pointer to data itself. When you use it, your code functions correctly:
use byteorder::{ByteOrder, BigEndian, LittleEndian};
use std::io::Read;
fn main(){
let random_bytes = (0..4).map(|_| { rand::random::<u8>() }).collect::<Vec<u8>>();
let random_bytes = random_bytes.as_slice();
let view = random_bytes.as_ptr() as *const u32;
let slice: &[u32] = unsafe { std::slice::from_raw_parts(view, 1) };
println!("{:x?}", slice);
println!("{:x?}", LittleEndian::read_u32(&random_bytes[0..4]));
println!("{:x?}", BigEndian::read_u32(&random_bytes[0..4]));
println!("{:x?}", &random_bytes[0..4]);
}
Output in playground:
[684a2f5b]
684a2f5b
5b2f4a68
[5b, 2f, 4a, 68]

Passing a Swift string to C

I am pretty new to Swift, and I don't have much exposure to C.
I am trying to write a function in C that will get a Swift string that I can then do something with. The problem is that I'm not 100% sure what the type should be in Swift to make C like what it sees.
So far, I have found several examples on Stack that seem like good starting points, but some examples seem dated for the current version of Swift.
I first started by using this example to get C and Swift talking to one another: Swift call C call Swift? I then took that and tried updating the Swift function to return a string of some kind. I understand that it needs to be a UTF-8 return type, but I'm not sure how to go about sending things properly. I've looked at How to pass a Swift string to a c function?, How to convert String to UnsafePointer<UInt8> and length, and How to convert string to unicode(UTF-8) string in Swift?, but none of them really work for a solution. Or I'm just typing it in incorrectly. So far, the closest I can get to returning something is as follows.
In Swift, my ViewController is:
import UIKit
class ViewController: UIViewController {
#_silgen_name("mySwiftFunc") // give the function a C name
public func mySwiftFunc(number: Int) -> [CChar]
{
print("Hello from Swift: \(number)")
let address: String = "hello there";
let newString = address.cString(using: String.Encoding.utf8)
return newString!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
blah()
}
}
And in C, the header is like:
#ifndef cfile_h
#define cfile_h
#include <stdio.h>
const char * mySwiftFunc(int);
int blah(void);
#endif /* cfile_h */
And the source is like:
#include "cfile.h"
int blah() {
const char * retVal = mySwiftFunc(42); // call swift function
printf("Hello from C: %s", retVal);
return 0;
}
There is a bridging header file that just has #include "cfile.h". Obviously, there is still a lot of remnants from the first example, and these will be cleaned up later.
What needs to change to make this work? Right now, the console spits out
Hello from Swift: 42
Hello from C: (B\214
The Swift equivalent of const char * is UnsafePointer<CChar>?, so that's the correct return value. Then you have to think about memory management. One options is do allocate memory for the C string in the Swift function, and leave it to the caller to release the memory eventually:
public func mySwiftFunc(number: Int) -> UnsafePointer<CChar>? {
print("Hello from Swift: \(number)")
let address = "hello there"
let newString = strdup(address)
return UnsafePointer(newString)
}
passes a Swift string to strdup() so that a (temporary) C string representation is created automatically. This C string is then duplicated. The calling C function has to release that memory when it is no longer needed:
int blah() {
const char *retVal = mySwiftFunc(42);
printf("Hello from C: %s\n", retVal);
free((char *)retVal);
return 0;
}
⚠️ BUT: Please note that there are more problems in your code:
mySwiftFunc() is an instance method of a class, and therefore has an implicit self argument, which is ignored by the calling C function. That might work by chance, or cause strange failures.
#_silgen_name should not be used outside of the Swift standard library, see this discusssion in the Swift forum.
A slightly better alternative is #_cdecl but even that is not officially supported. #_cdecl can only be used with global functions.
Generally, calling Swift functions directly from C is not officially supported, see this discussion in the Swift forum for the reasons and possible alternatives.

Swift 3 - No 'sort' candidates produce the expected contextual result type 'NSMutableArray'

I am attempting to sort a mutable array in Swift 3.1.1, but get the same error every time:
No 'sort' candidates produce the expected contextual result type 'NSMutableArray'.
Is there a way to sort a mutable array (Ints only) in ascending order?
In my code, elements from options are being removed. Removed (the array) is adding the removed elements. At the end of the code I am attempting to add the elements from the removed array back to options and sort it.
// set up tiles
var options = NSMutableArray()
var removed = NSMutableArray()
for i in 1...49 {
options.add(i as Int)
print("options\(options.count)")
}
for i in 1...49 {
print("i = \(i)")
options.remove(i)
let tilea: Int = options[Int(arc4random_uniform(UInt32(options.count)))] as! Int
options.remove(tilea)
let tileb: Int = options[Int(arc4random_uniform(UInt32(options.count)))] as! Int
options.remove(tileb)
removed.add([i, tilea, tileb])
print(options.count)
if options.count < 20 {
options.add(removed)
options = options.sort {
$0 < $1
}
}
}
As already mentioned, in Swift you should really be using the Array<T> for this (aka, [T]) instead of NSMutableArray. For instance:
var options = [Int]()
when adding elements to it, use append (and, by the way, you can drop the type cast as well):
options.append(i)
options.append(contentsOf: [i, j, k])
finally, when sorting the array, use the sort function (it doesn't return a value; the array is sorted in-place):
options.sort()
and you don't need even to provide a comparation function since integers conform to the Comparable protocol.
NSMutableArray, among other Objective C types, was implicitly bridged to/from its Swift counterparts. In a move to lessen peoples (usually unnecessary) reliance on these Objective C types, this implicit bridging has been changed in Swift 3, and now needs an explicit type coercion (e.g nsArray as [Int])

Why to use tuples when we can use array to return multiple values in swift

Today I was just going through some basic swift concepts and was working with some examples to understand those concepts. Right now I have completed studying tuples.
I have got one doubt i.e, what is the need of using tuples ? Ya I did some digging on this here is what I got :
We can be able to return multiple values from a function. Ok but we can also do this by returning an array.
Array ok but we can return an multiple values of different types. Ok cool but this can also be done by array of AnyObject like this :
func calculateStatistics (scores:[Int])->[AnyObject]
{
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores
{
if score > max{
max = score
}
else if score < min{
min = score
}
sum += score
}
return [min,max,"Hello"]
}
let statistics = calculateStatistics([25,39,78,66,74,80])
var min = statistics[0]
var max = statistics[1]
var msg = statistics[2] // Contains hello
We can name the objects present in the tuples. Ok but I can use a dictionary of AnyObject.
I am not saying that Why to use tuples when we have got this . But there should be something only tuple can be able to do or its easy to do it only with tuples. Moreover the people who created swift wouldn't have involved tuples in swift if there wasn't a good reason. So there should have been some good reason for them to involve it.
So guys please let me know if there's any specific cases where tuples are the best bet.
Thanks in advance.
Tuples are anonymous structs that can be used in many ways, and one of them is to make returning multiple values from a function much easier.
The advantages of using a tuple instead of an array are:
multiple types can be stored in a tuple, whereas in an array you are restricted to one type only (unless you use [AnyObject])
fixed number of values: you cannot pass less or more parameters than expected, whereas in an array you can put any number of arguments
strongly typed: if parameters of different types are passed in the wrong positions, the compiler will detect that, whereas using an array that won't happen
refactoring: if the number of parameters, or their type, change, the compiler will produce a relevant compilation error, whereas with arrays that will pass unnoticed
named: it's possible to associate a name with each parameter
assignment is easier and more flexible - for example, the return value can be assigned to a tuple:
let tuple = functionReturningTuple()
or all parameters can be automatically extracted and assigned to variables
let (param1, param2, param3) = functionReturningTuple()
and it's possible to ignore some values
let (param1, _, _) = functionReturningTuple()
similarity with function parameters: when a function is called, the parameters you pass are actually a tuple. Example:
// SWIFT 2
func doSomething(number: Int, text: String) {
println("\(number): \(text)")
}
doSomething(1, "one")
// SWIFT 3
func doSomething(number: Int, text: String) {
print("\(number): \(text)")
}
doSomething(number: 1, text: "one")
(Deprecated in Swift 2) The function can also be invoked as:
let params = (1, "one")
doSomething(params)
This list is probably not exhaustive, but I think there's enough to make you favor tuples to arrays for returning multiple values
For example, consider this simple example:
enum MyType {
case A, B, C
}
func foo() -> (MyType, Int, String) {
// ...
return (.B, 42, "bar")
}
let (type, amount, desc) = foo()
Using Array, to get the same result, you have to do this:
func foo() -> [Any] {
// ...
return [MyType.B, 42, "bar"]
}
let result = foo()
let type = result[0] as MyType, amount = result[1] as Int, desc = result[2] as String
Tuple is much simpler and safer, isn't it?
Tuple is a datastructure which is lighter weight than heterogeneous Array. Though they're very similar, in accessing the elements by index, the advantage is tuples can be constructed very easily in Swift. And the intention to introduce/interpolate this(Tuple) data structure is Multiple return types. Returning multiple data from the 'callee' with minimal effort, that's the advantage of having Tuples. Hope this helps!
A tuple is ideally used to return multiple named data from a function for temporary use. If the scope of the tuple is persistent across a program you might want to model that data structure as a class or struct.

Resources