从Swift 4调用C函数

6

我使用了codegen工具从MATLAB生成C代码。这个函数可以描述为:

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

使用代码生成工具时,我明确声明my_matrix是double(:inf, 3)类型。换句话说,行数是无限的,但它将有3列。当代码生成时,这是我要执行的函数:

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

在另一个c文件中,emxArray_real_T的定义如下:

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

当我看到上述类的初始化选项时,其中一个选项特别合理:
emxArray_real_T.init(data: UnsafeMutablePointer<Double>!, size: UnsafeMutablePointer<Int32>!, allocatedSize: Int32, numDimensions: Int32, canFreeData: boolean_T)

我试图按照这篇文档的指导来理解如何调用生成的C代码,但我认为我可能错过了一些基本步骤。以下是我的操作:

// 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)

然而,上述代码会抛出一个错误,指出:
Generic parameter 'C' could not be inferred

我认为我做错了一些事情,可能走错了方向。有一个类似的帖子与我的问题相关: 如何将float[][]类型的数组转换为"emxArray_real_T *x",但是提供的解决方法似乎是针对C而不是Swift 4的。如何使用Swift 4有效地调用C函数,并满足emxArray_real_T.init方法的要求?使用虚假数据来演示基本原理是可以的。

在尝试帮助之前,我想确认一些事情。我已经多年没有使用MATLAB了,但是看起来该函数将my_matrix缩减为类型为double的1x1矩阵(result)。然而,生成的代码更通用,允许从MxN到KxL矩阵的转换。这些假设正确吗?此外,生成的calculate_data函数必须是C函数,但它没有显示;相反,只给出了它的Swift表示形式。C函数是什么样子的? - Anatoli P
1个回答

5
在一个简单的Xcode项目中,使用模拟的C结构体emxArray_real_T和函数calculate_data,我可以成功地运行以下代码。要创建类型为emxArray_real_T的对象,我可以执行以下操作:
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
)

这个对象可以像这样传递给函数calculate_datacalculate_data(&array, nil)。在实际应用中,nil将是另一个数组对象。为了简单起见,这里只用作占位符。

通过使用正确的类型(第6行中使用[Double]而不是Double),您的第二个问题可以得到解决:

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()

输出结果将会是:
[0.0, 0.0, 0.0]
[1.0, 2.0, 3.0]
[2.0, 4.0, 6.0]

顺理成章。

我必须承认,我使用的是Swift 5.0.1。虽然这不会产生很大的差异。


关于你的第一个代码块,初始化数据数组的部分。你是说目的是创建一个一维数组,当我们指定大小时,它才被视为矩阵?所以看这段代码,你说它是4 x 3。我理解1:3,1 = 0:2,1:3,2 = 3:5。这意味着我们将其初始化为一个一维数组,但大小规定了映射关系。这听起来正确吗? - angryip
不用在意,它确实是这样工作的。我最终查看了C代码,这听起来是正确的。 - angryip
确切地说,这就是它的工作原理。在这里您可以找到有关如何解释此结构的说明。您还可以在那里找到一些实用程序函数,以更简单的方式创建对象。顺便说一下,使用1d数组存储数据是一个典型的“模式”。大多数数值C库都是这样做的。一个数组访问比两个(甚至多个n维矩阵)更便宜。 - SimplyDanny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接