从 Swift 4 调用 C 函数

Posted

技术标签:

【中文标题】从 Swift 4 调用 C 函数【英文标题】:Call C functions from Swift 4 【发布时间】:2019-07-26 01:35:07 【问题描述】:

我使用 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

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

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

emxArray_real_T 在不同的 c 文件中定义如下:

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)

我尝试关注this document,以此来了解如何调用生成的 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

我认为我在做一些完全错误的事情,并且可能走上了不正确的道路。有一个类似的帖子与我的问题有关,How to convert float[][] type array to "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 函数是什么样的? 【参考方案1】:

在一个简单的 Xcode 项目中,为结构 emxArray_real_T 和函数 calculate_data 模拟 C 构造,我可以成功运行以下代码。要创建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_data(&amp;array, nil)一样传递给函数calculate_data。在实际应用程序中,nil 将是另一个数组对象。为简单起见,这里仅用作占位符。

您的第二个问题可以通过使用正确的类型来解决([Double] 而不是第 6 行中的 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 。这意味着我们将其初始化为一维数组,但大小决定了映射。听起来对吗? nvm,它确实是这样工作的。我开始查看 c 代码,这听起来很正确。 没错,这就是它的工作原理。 Here 您可以找到有关如何解释此结构的解释。您还可以在那里找到一些实用功能,以更简单的方式创建对象。顺便说一句,使用一维数组存储数据是一种典型的“模式”。大多数数字 C 库都是这样做的。一个数组访问比两个更便宜(对于 n 维矩阵甚至更多)。

以上是关于从 Swift 4 调用 C 函数的主要内容,如果未能解决你的问题,请参考以下文章

从 C 调用 Swift 的最佳方法是啥?

从 swift 4 调用贝宝 javascript 函数

从 Swift 调用带有数组指针和 int 指针的 C 函数

从 Swift 5.3 版调用 C 函数:使用 pthreads

从 Swift 调用 Objective C 自定义初始化函数

从objective-c++调用swift函数