Преобразовать Int8? в UnsafePointer?

#c #swift #xcode

#c #swift #xcode

Вопрос:

Я очень новичок в Swift. Пытаюсь написать приложение Swift, которое вызывает внешний метод C. Однако компилятор Xcode выдает ошибку о преобразовании типов. Прототип метода C имеет вид:

 void * cMethod(const char* string1Path, const char* string2Path, const char* string3Path, const char* string4Path);
  

Суть кода Swift заключается в:

 import OpenGLES
import CoreGraphics
import GLKit

var mGLUTWindow: Void?

class myClass {

    mGLUTWindow = nil
    let appBundle = Bundle.main

    let string1 = appBundle.path(forResource: "Init", ofType: "dat")
    let string1Path = Int8((string1?.description)!)

    let string2 = appBundle.path(forResource: "autostart", ofType: "type")
    let string2Path = Int8((string2?.description)!)

    let string3: String? = appBundle.path(forResource: "data", ofType: "") ?? ""   ("/")
    let string3Path = Int8((string3?.description)!)

    mGLUTWindow = cMethod(UnsafePointer(string3Path), string1Path, string2Path, "ios")
}
  

Компилятор выдает ошибку:

 Cannot convert value of type 'Int8?' to expected argument type 'UnsafePointer<Int8>?'
  

Метод C вводится с помощью связующего заголовка. Есть ли способ преобразовать это в ожидаемый аргумент?

ПРИМЕЧАНИЕ: приведенный здесь код Swift был перенесен из файла Objective-C несколько лет назад и адаптируется к Swift. Я использовал этот конвертер Obj-C в Swift, так как я едва понимаю Swift и не могу читать Obj-C. Подкладка Obj-C, которую я вставил, имела вид:

 NSString * string1 = [[appBundle pathForResource:@"data" ofType:@""] stringByAppendingString:@"/"]; 
const char * string1Path = [string1 cStringUsingEncoding:[NSString defaultCStringEncoding]];
  

Комментарии:

1. Почему вы пытаетесь преобразовать путь в целое число? Это никогда не сработает.

2. Извините, это просто символ. Не путь.

3. Вы вводите его из пути здесь: Int8((string1?.description)!) . Это будет давать вам nil каждый раз.

4. Итак, для этого кода Swift я использую файл Objective-C, созданный несколько лет назад, и пытаюсь адаптировать его к Swift. Я использовал этот конвертер obc-c 2 Swift ( objectivec2swift.com/#/converter/code ), так как я едва понимаю Swift и не могу читать obj-c. Строка obj-c, которую я ввел, была: NSString * string1 = [[appBundle pathForResource:@"data" ofType:@""] stringByAppendingString:@"/"]; const char * string1Path = [string1 cStringUsingEncoding:[NSString defaultCStringEncoding]];

5. Вам лучше включить такую важную информацию в свой вопрос. Вы можете редактировать и обновлять свой собственный вопрос. Лучше сделайте это.

Ответ №1:

Если бы мне дали C-функцию и некоторый код, отображаемый в виде ваших строк Objective-C, я бы написал что-то вроде этого:

 
var mGLUTWindow: UnsafeRawPointer?

class MyClass {

    func aMethod() {
        let appBundle = Bundle.main

        let string1 = appBundle.path(forResource: "Init", ofType: "dat")!

        let string2 = appBundle.path(forResource: "autostart", ofType: "type")!

        let string3 = (appBundle.path(forResource: "data", ofType: "") ?? "")   ("/")

        mGLUTWindow = UnsafeRawPointer(cMethod(string3, string1, string2, "ios"))
    }
}
  

В Swift, когда вы переходите String к аргументу типа char * , Swift генерирует временный буфер C-string и передает адрес буфера, вам не нужно вызывать что-то вроде const char * string1Path = [string1 cStringUsingEncoding:[NSString defaultCStringEncoding]]; .

Если cMethod какой-либо из указателей сохраняется для последующего использования, вам может понадобиться немного более сложный код, но неясно, нужен ли нам такой сложный код, как для текущего описания.

Ответ №2:

Хотя преобразование an Int8? в указатель достаточно просто (просто разверните его и используйте amp; оператор при передаче его в качестве аргумента), ваша реальная проблема заключается в том, что преобразование вашей строки в an Int8 в первую очередь является ошибкой. Возвращаемое значение Bundle.path(forResource:ofType:) будет представлять собой путь, выглядящий примерно так:

 "/Applications/MyApp.app/Contents/Resources/Init.dat"
  

Между тем, вы пытаетесь преобразовать это в an Int8 , который представляет собой целочисленный тип, который может хранить значения от -128 до 127, используя инициализатор, который принимает a String . Причина, по которой этот инициализатор возвращает необязательное значение, заключается в том, что не каждая строка будет содержать число от -128 до 127, поэтому, если строка является чем-то другим, вы получаете nil .

Строка, подобная "37" или "-101" , будет преобразована правильно, но строка пути, подобная приведенной выше, всегда будет просто давать вам nil .

Что вы на самом деле хотите сделать здесь, это преобразовать ваш String в строку C. Есть несколько разных способов сделать это, но я бы сделал это так, чтобы использовать URL fileSystemRepresentation функцию ‘s, например:

 let url1 = appBundle.url(forResource: "Init", withExtension: "dat")! // instead of path
url1.withUnsafeFileSystemRepresentation { cPath in
    // cPath here is an UnsafePointer<Int8> that you can pass to C APIs inside this block.
}

// Don't let cPath get out here or bad things will happen.
  

Обратите внимание, что вам нужно убедиться, что здесь не разрешено cPath экранировать withUnsafeFileSystemRepresentation блок, потому что он не определен как допустимый вне этой области.

Также обратите внимание, что из-за ! этого произойдет сбой вашего приложения, если Init.dat файл фактически не существует внутри вашего приложения, поэтому вам лучше убедиться, что вы его включили.