Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call C functions from Swift 4

Tags:

c

swift

matlab

I've generated C code from MATLAB by using the codegen tool. The function can be described as follows:

function [result] = calculate_data(my_matrix)
    for idx = 1:length(my_matrix)
        result = result + sum(my_matrix(idx,1:3));
    end
end

When using the codegen tool, I explicitly stated that my_matrix is a type of double(:inf, 3). In other words, the number of rows is unbounded, but it will have 3 columns. When the code is generated, this is the function that is generated that I am to execute:

calculate_data(my_matrix : UnsafePointer<emxArray_real_T>!, result : UnsafeMutablePointer<emxArray_real_T>!)

emxArray_real_T is defined as follows in a different c file:

struct emxArray_real_T
{
  double *data;
  int *size;
  int allocatedSize;
  int numDimensions;
  boolean_T canFreeData;
};

When I see my initialization options for the above class, this one in particular makes sense:

emxArray_real_T.init(data: UnsafeMutablePointer<Double>!, size: UnsafeMutablePointer<Int32>!, allocatedSize: Int32, numDimensions: Int32, canFreeData: boolean_T)

I've tried to follow this document as a means to wrap my head around how to call the generated C code, but I think I might be missing a basic step. Here is what I am doing:

// create an 2d array with some fake data
var mySampleData = [[Double]]();
for i in 0 ..< 3 {
    mySampleData.append([1.1, 2.2, 3.3]);
}

// begin fulfilling requirements for emxArray_real_T
var data_pointer = UnsafeMutablePointer<Double>.allocate(capacity: 3);
data_pointer.initialize(from: mySampleData)

However, the above code throws an error stating that:

Generic parameter 'C' could not be inferred

I take it that I am then doing something completely wrong, and am probably on an incorrect path. There is a similar post that relates to my question, How to convert float[][] type array to "emxArray_real_T *x" , however the provided solution seems to be for C, as opposed to for Swift 4. How can I effectively call a C function using Swift 4, and meet the requirements of the emxArray_real_T.init method? Using fake data is ok to demonstrate the basic principle.

like image 765
angryip Avatar asked Jul 26 '19 01:07

angryip


1 Answers

In a simple Xcode project with mocked C constructs for the struct emxArray_real_T and the function calculate_data I can run the following code successfully. To create an object of type emxArray_real_T I do

var data: [Double] = (0 ..< 12).map(Double.init)
var size: [Int32] = [4, 3]

var array = emxArray_real_T(
    data: &data,
    size: &size,
    allocatedSize: 12,
    numDimensions: 2,
    canFreeData: false
)

This object can be passed to the function calculate_data like calculate_data(&array, nil). In a real application nil would be another array object. For the sake of simplicity it is just used as a placeholder here.

Your second issue can be solved by using the right types ([Double] instead of Double in line 6):

var mySampleData = [[Double]]();
for i in 0 ..< 3 {
    mySampleData.append([i*1, i*2, i*3].map(Double.init));
}

let pointer = UnsafeMutablePointer<[Double]>.allocate(capacity: 3)
pointer.initialize(from: mySampleData, count: 3)

print((pointer + 0).pointee)
print((pointer + 1).pointee)
print((pointer + 2).pointee)

pointer.deallocate()

The output will be

[0.0, 0.0, 0.0]
[1.0, 2.0, 3.0]
[2.0, 4.0, 6.0]

as expected.

I have to admit that I used Swift 5.0.1. This should not make significant differences, though.

like image 171
SimplyDanny Avatar answered Oct 23 '22 23:10

SimplyDanny