首页 > 解决方案 > Swift:将已分配的字符数组转换为字符指针

问题描述

我正在尝试将现有的 C 库连接到 iOS 上的 Swift 5.0.1 代码。C 头文件具有以下定义:

char hostname[SFL_MAX_HOSTNAME_CHARS+1];
char os_release[SFL_MAX_OSRELEASE_CHARS+1];
int readHidCounters(HSP *sp, SFLHost_hid_counters *hid, char *hbuf, int hbufLen, char *rbuf, int rbufLen);
typedef struct _HSP {
    [Many other elements omitted for brevity]
    char hostname[SFL_MAX_HOSTNAME_CHARS+1];
    char os_release[SFL_MAX_OSRELEASE_CHARS+1];
  } HSP;

readHidCounters有一个像这样的实现(为简洁而编辑):

int readHidCounters(HSP *sp, SFLHost_hid_counters *hid, char *hbuf, int hbufLen, char *rbuf, int rbufLen) {
    int gotData = NO;
    size_t len = hbufLen;
    if(sysctlbyname("kern.hostname", hbuf, &len, NULL, 0) != 0) {
      myLog(LOG_ERR, "sysctl(<kern.hostname>) failed : %s", strerror(errno));
    }
    else {
      gotData = YES;
      hid->hostname.str = hbuf;
      hid->hostname.len = strlen(hbuf);
    }
    // UUID
    memcpy(hid->uuid, sp->uuid, 16);

    [...]
}

我创建了一个 HSP 结构并尝试readHidCounters像这样在 Swift中调用

var sp = HSP()
[...]        
readHidCounters(&sp,
                &hidElem.counterBlock.host_hid,
                &sp.hostname, // This is the error line
                SFL_MAX_HOSTNAME_CHARS,
                &sp.os_release,
                SFL_MAX_OSRELEASE_CHARS)

我试图&sp.hostname在编译器错误中传递结果Cannot convert value of type '(Int8, Int8, Int8, [...], Int8)' to expected argument type 'Int8'。问题是主机名是 Int8 的元组,我似乎无法将其正确转换为char *. 我尝试了 的各种化身UnsafeMutablePointerwithUnsafeMutablePointer但看不到如何hostname正确识别。任何建议都非常感谢!

[解决了]

MartinR 几乎用他的建议解决了这个问题,但它确实有一个编译器错误:Overlapping accesses to 'sp.hostname', but modification requires exclusive access; consider copying to a local variable. 编译的更新代码是

        var myHostName = sp.hostname
        var myOsRelease = sp.os_release
        let _ = withUnsafeMutablePointer(to: &myHostName) {
            $0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.hostname)) {
                hostNamePtr in
                withUnsafeMutablePointer(to: &myOsRelease) {
                    $0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.os_release)) {
                        osReleasePtr in
                        readHidCounters(&sp,
                                        &hidElem.counterBlock.host_hid,
                                        hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
                                        osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
                    }
                }
            }
        }

标签: cswift

解决方案


“问题”是 C 数组作为元组导入到 Swift 中,没有简单的方法可以将元组视为 Swift 数组,或者获取指向元素存储的指针(因为元组可能是不均匀的)。

与将 C 字符数组转换为字符串类似,可以使用 Swift 保留从 C 导入的结构的内存布局这一事实,并且通过一些指针杂耍和重新绑定,您将得到

let result = withUnsafeMutablePointer(to: &sp.hostname) {
    $0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.hostname)) {
        hostNamePtr in
        withUnsafeMutablePointer(to: &sp.os_release) {
            $0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.os_release)) {
                osReleasePtr in
                readHidCounters(&sp,
                                &hidElem.counterBlock.host_hid,
                                hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
                                osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
            }
        }
    }
}

另一个“技巧”是定义将数组地址作为指针返回的 C 辅助函数,并使用

使这些辅助函数可用作 Swift 计算属性的功能。在桥接头文件中,您必须添加

__attribute__((swift_name("getter:HSP.hostNamePtr(self:)")))
static inline char * _Nonnull hostNamePtr(HSP * _Nonnull hsp)
{
    return hsp->hostname;
}

__attribute__((swift_name("getter:HSP.osReleasePtr(self:)")))
static inline char * _Nonnull osReleasePtr(HSP * _Nonnull hsp)
{
    return hsp->os_release;
}

然后你可以从 Swift 中轻松使用这些:

var sp = HSP()

let result = readHidCounters(&sp,
                          &hidElem.counterBlock.host_hid,
                          sp.hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
                          sp.osReleasePtr, SFL_MAX_OSRELEASE_CHARS)

推荐阅读