【问题标题】:Swift: convert already allocated char array to char pointerSwift:将已分配的字符数组转换为字符指针
【发布时间】:2019-11-21 19:22:43
【问题描述】:

我正在尝试将现有的 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 结构并尝试像这样在 Swift 中调用 readHidCounters

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)

我试图传入&amp;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)
                    }
                }
            }
        }

【问题讨论】:

  • API 对我来说不是很清楚。如果 hostname 和 os_release 是 HSP 结构的一部分(作为第一个参数传递给函数),那么为什么还要将这些数组作为参数传递给函数呢?
  • @MartinR 我已经添加了readHidCounters 的部分实现。你说的是真的,可能实现者不想跟sp这么紧密的耦合?
  • 也许我不熟悉那个库。 – 我提出了一些建议,但我只能验证它是否编译, 而不是它适用于你的情况。

标签: c swift


【解决方案1】:

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

Converting a C char array to a String 类似,可以使用 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)

【讨论】:

  • 您的解决方案符合我的一些尝试,但您明白了。对于一些小的编译器投诉(添加到问题的工作代码),我不得不对其进行一些调整。谢谢!
【解决方案2】:

尝试从调用中删除 &,sp.hostname 已经是一个指针,因为它是一个数组,如果它有效,则必须从 &sp.os_release em>也是

【讨论】:

  • 不幸的是,这不起作用。删除 &amp; 会给出错误“无法将类型 '(Int8, Int8, [...], Int8)' 的值转换为预期的参数类型 'UnsafeMutablePointer?'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多