Лучший способ передать адрес "char path [MAX]" параметру "const char *" в Swift - PullRequest
2 голосов
/ 14 июня 2019

Я понимаю, что существует миллион вариаций вопроса «как мне конвертировать char[] / char* в стремительный укус» и их обратный вопрос, на который все были заданы и даны ответы.

Я этого не спрашиваю.

Все, что я хочу сделать в Swift, - это просто передать адрес массива C char (полученный с помощью функции C) аргументу указателя C char* другой функции C.

В частности, я пытаюсь скопировать следующий код C, где адрес массива char, содержащийся в поле stat.f_mntonname, передается в качестве первого параметра вызова getattrlist(const char*, ...):

// Get volume stat
const char* path = ...;
struct statfs volStat;
if (statfs(path,&volStat)==-1) { error }

// statfs has the mount point of the volume; use that to get attributes
struct attrlist request;
// ... set up request here
struct volAttrs {
    // ... response values
}

if (getattrlist(volStat.f_mntonname,&request,&volAttrs,sizeof(volAttrs),FSOPT_NOFOLLOW)==-1) { error }

Проблема, похоже, заключается в том, что Swift интерпретирует поле stat.f_mntonname не как массив, а как кортеж, содержащий MAXPATHLEN количество Int8 значений; другими словами, (Int8,Int8,Int8,Int8,Int8,...,Int8).

После долгих раздумий в интернете, я смог найти этот обходной путь:

var volStat = statfs()
guard statfs(fileURL.path, &volStat) != -1 else {
    ErrorExit(cause: "statfs")
}
var attrRequest = attrlist()
// set up getattrlist request ...
var attrs = XtraVolumeAttrs()
guard getattrlist(UnsafeRawPointer(&volStat.f_mntonname.0).bindMemory(to: CChar.self, capacity: Int(MAXPATHLEN)),
                  &attrRequest,
                  &attrs,
                  MemoryLayout<XtraVolumeAttrs>.size,
                  UInt32(FSOPT_NOFOLLOW)) != -1 else {
    ErrorExit(cause: "getattrlist")
}

Таким образом, магия UnsafeRawPointer(&volStat.f_mntonname.0).bindMemory(to: CChar.self, capacity: Int(MAXPATHLEN), кажется, выполняет задачу преобразования массива char[MAXPATHLEN] в char*, но мальчик это уродлив, не интуитивен, и - если я полностью честен - я даже не уверен, что это правильно (кроме факта, что код работает).

Я чувствую, что должен быть лучший способ, и я надеялся, что кто-то опубликует его.

1 Ответ

1 голос
/ 14 июня 2019

Это ужасно, потому что Swift импортирует массивы C в виде кортежей, и нет автоматического преобразования в массивы или указатели.

Как указал Хэмиш, использование UnsafeRawPointer(&volStat.f_mntonname.0) некорректно, поскольку созданный указатель может быть недействительным при возврате из инициализатора.

Безопасная версия -

let retval = withUnsafeBytes(of: volStat.f_mntonname) { 
    getattrlist($0.bindMemory(to: Int8.self).baseAddress, /* other args */)
}

Здесь bindMemory() вызывается для «указателя буфера», охватывающего необработанные байты кортежа, так что нам не нужно явно указывать емкость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...