Быстрое чтение char (не char *) как указатель строки C

Я только начал с Swift на этой неделе, в частности Swift 4, и я использую библиотеку C через мостовой заголовок, liblo, который обрабатывает отправку / получение сообщений в формате OSC (Open Sound Control) через сетевой сокет.

Я могу запустить серверный поток, получать сообщения OSC через обратный вызов C-> Swift и читать числовые значения аргументов с помощью Swift. Однако у меня проблемы с чтением строковых значений.

Тип аргумента сообщения liblo lo_arg - это определение типа C для объединения, а строковые типы аргументов объявляются как простые символы, которые отображаются в Swift как Int8.

В C вы можете получить строку через &argv[i]->s из массива lo_arg **argv вашего обратного вызова. В проекте Obj-C с liblo я использую:

// get string value of first argument
lo_arg arg = argv[0];
NSString *s = [NSString stringWithUTF8String:&arg->s];
// do something with s

В Swift я попытался получить адрес Int8 и передать его String, который работает, но захватывает только первый символ:

// get string value of first argument
if var arg : lo_arg = argv?.pointee![0] {
    withUnsafePointer(to: &arg.s) {
        let s = String(cString: $0)
        // so something with s
    }
}

Я делаю что-то неправильно? Я бы подумал, что это будет эквивалентно, но передача $0 в strlen() ala print("strlen: \(strlen($0)") выводит только длину «1». Я подтвердил, что многосимвольная строка действительно отправляется с помощью тестовой программы, отличной от Swift. Теперь мне интересно, предполагает ли Swift каким-то образом, что строка является одним символом вместо адреса заголовка строки C, и / или мне нужно дополнительное преобразование указателя.


person danomatika    schedule 16.03.2018    source источник


Ответы (1)


Покопавшись, я могу подтвердить, что Swift усекает строковые значения lo_arg->s & lo_arg->S до 8 байтов в моей 64-битной системе, также известной как sizeof(char). Это происходит при попытке прочитать строку из lo_arg из Swift. Чтение того же значения в C работает нормально, поэтому Swift, похоже, резервирует / разрешает чтение только из пространства для одного символа. Пересылка lo_arg из Swift в C и печать строки через printf() также показывает усеченные строки до 8 символов.

Быстрое исправление состоит в том, чтобы избежать чтения строк из lo_arg, сгенерированного Swift, взять lo_arg из необработанного lo_message в C и привести «указатель» char к const char * Swift будет понимать как строку переменной длины. Вот несколько рабочих служебных функций, которые я добавил в свой заголовок моста:

/// return an lo_message argv[i]->s in a format Swift can understand as a String
const char* lo_message_get_string(lo_message message, int at) {
    return (const char *)&lo_message_get_argv(message)[at]->s;
}

/// return an lo_message argv[i]->S in a format Swift can understand as a String
const char* lo_message_get_symbol(lo_message message, int at) {
    return (const char *)&lo_message_get_argv(message)[at]->S;
}

Затем в Swift я могу преобразовать в строку:

let s = String(cString: lo_message_get_string(msg, 0))
// do something with s
person danomatika    schedule 21.03.2018