Konvertieren Sie das 2D-Array von Swift in das 2D-Array von C.

$ swift --version
Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36)
Target: x86_64-apple-macosx10.9

Swift ist praktisch, weil es das Aufrufen von C-Sprachfunktionen erleichtert. Sie können Swifts Array auch an eine Funktion übergeben, die einen Zeiger in C empfängt, sodass das Übergeben eines Arrays einfach ist.

Dies wird durch den Mechanismus erreicht, dass "Array " im Funktionsaufrufteil implizit in "UnsafePointer " umgewandelt wird.

Aber wenn es um ein zweidimensionales Array geht, funktioniert es nicht. Doppelzeiger werden verwendet, wenn ein zweidimensionales Array in C empfangen wird. Zum Beispiel in C.

void f0(const int * const * p);

Aus Swifts Sicht ist die Funktion definiert als

func f0(_ p: UnsafePointer<UnsafePointer<CInt>?>!)

Wird interpretiert als. Wenn sich auf der Swift-Seite ein let arr: Array <Array <CInt >> befindet, funktioniert Folgendes nicht.

let arr: Array<Array<CInt>> = [ [0, 1, 2], [3, 4, 5] ]
f0(arr) //Kompilierungsfehler

Denn was Sie erhalten, wenn Sie "Array <Array >" implizit in einen Zeiger konvertieren, ist

UnsafePointer<Array<CInt>>

Und

UnsafePointer<UnsafePointer<CInt>?>

Weil es nicht ist.

Wenn Sie sich also die Transformation ansehen, die "UnsafePointer " von "Array " abruft,

func withUnsafeBufferPointer<R>(_ body: (UnsafeBufferPointer<Array.Element>) throws -> R) rethrows -> R

Kann gefunden werden. Sie können es verwenden, um Code wie folgt zu schreiben: Dies ** wird kompiliert, sollte aber nicht. ** ** **

let arr: Array<Array<CInt>> = [ [0, 1, 2], [3, 4, 5] ]
let bufPtrArr: Array<UnsafeBufferPointer<CInt>> = arr.map {
    return $0.withUnsafeBufferPointer { $0 }
}
let ptrArr: Array<UnsafePointer<CInt>?> = bufPtrArr.map { $0.baseAddress }
f0(ptrArr)

Dies liegt daran, dass der Zeiger, der an das Schließen des Arguments der Methode "withUnsafeBufferPointer" übergeben wird, nur während der Ausführung dieser Methode verwendet werden sollte.

Es wird für rekursive Aufrufe verwendet, da Sie die Arrays einzeln in Zeiger konvertieren müssen, die nur während "withUnsafeBufferPointer" verwendet werden können. Nach dem Konvertieren eines Arrays mit "withUnsafeBufferPointer" besteht die Prozedur darin, ein Array von Zeigern zu erstellen, indem das nächste Array innerhalb der Methode konvertiert wird.

Dieser rekursive Prozess ist oft schwer zu schreiben. Machen wir ihn also zu einer Erweiterungsmethode von "Array" zur Wiederverwendung und holen "Array <UnsafeBufferPointer >" von "Array <Array >". Ich habe withUnsafeBufferPointerArray gemacht.

extension Array {
    public func withUnsafeBufferPointerArray<T, R>(_ body: (Array<UnsafeBufferPointer<T>>) -> R) -> R
        where Element == Array<T>
    {
        var buffers = Array<UnsafeBufferPointer<T>>()
        var result: R?
        
        func recurse(body: (Array<UnsafeBufferPointer<T>>) -> R)
        {
            let i = buffers.count
            guard i < self.count else {
                result = body(buffers)
                return
            }
            self[i].withUnsafeBufferPointer { (buf: UnsafeBufferPointer<T>) in
                buffers.append(buf)
                recurse(body: body)
            }
        }
        
        recurse(body: body)
        
        return result!
    }
}

Die Verwendung einer In-Function-Funktion erleichtert das Schreiben eines Prozesses, der diese Art von Statusoperation und rekursivem Aufruf kombiniert. Ich mag es nicht, den Rückgabewert so oft zu kopieren, wie es Elemente gibt, also verwende ich "Optional" und "!".

Jetzt können Sie "f0" wie folgt aufrufen.

let arr: Array<Array<CInt>> = [ [0, 1, 2], [3, 4, 5] ]
arr.withUnsafeBufferPointerArray { (bufPtrArr: Array<UnsafeBufferPointer<CInt>>) in
    let ptrArr: Array<UnsafePointer<CInt>?> = bufPtrArr.map { $0.baseAddress }
    f0(ptrArr)
}

Als praktisches Beispiel OpenGL ES

void glShaderSource(
	GLuint shader,
	GLsizei count,
	const GLchar * const *string,
	const GLint *length);

Bei Anruf

    public static func shaderSource(_ id: GLuint,
                                    _ strings: Array<Array<GLchar>>)
    {
        strings.withUnsafeBufferPointerArray { (buffers: Array<UnsafeBufferPointer<GLchar>>) in
            glShaderSource(id,
                           GLsizei(buffers.count),
                           buffers.map { $0.baseAddress },
                           buffers.map { GLint($0.count) })
        }
    }

Es ist wie es ist. Es ist schön, dass die Argumente "count", "string" und "length" leicht aus "Puffern" konstruiert werden können.

Recommended Posts

Konvertieren Sie das 2D-Array von Swift in das 2D-Array von C.
Konvertiert vom String-Zeiger der C-Sprache zum Swift-String-Typ
[Java] Konvertiert ArrayList in Array
Übergeben Sie C-Sprache char * an Swift
Konvertieren Sie das Alphabet in 26 Basis + ungefähr Array-Länge
Konvertieren Sie ein zweidimensionales Array mit der Java 8 Stream-API in das CSV-Format
[Java] Konvertieren Sie ein Array in ArrayList * Achtung
Konvertieren Sie die C-Sprache mit Emscripten in JavaScript
Konvertieren Sie ein Array von Zeichenfolgen in Zahlen
Konvertieren Sie das Array von error.full_messages in Zeichen und geben Sie es aus
So konvertieren Sie eine Datei in ein Byte-Array in Java
Generieren Sie Modelle von JSON zu Swift, PHP, C #, JAVA
[Java] Ich möchte ein Byte-Array in eine Hexadezimalzahl konvertieren
Port C-Code mit vielen Typecasts zu Swift
Konvertieren Sie ein Java-Byte-Array in eine hexadezimale Zeichenfolge
Konvertieren Sie ein potenziell null-Array in einen Stream
Anything Arrangement-11 [C # Refactoring-Beispiel]
ABC - 133 - A & B & C & D.
Ich möchte Zeichen konvertieren ...
Konvertieren Sie den Zeichenfolgentyp in den Zeitstempeltyp
In Ruby Leet-Zeichenfolge konvertieren
Konvertieren Sie ein serialisierbares Objekt in Byte []
ABC - 122 - A & B & C & D.
Konvertieren Sie von ○ Monaten in ○ Jahre ○ Monate
ABC - 125 - A & B & C & D.
ABC - 130 - A & B & C & D.
ABC - 126 - A & B & C & D.
So initialisieren Sie ein Java-Array
[Swift5] So erhalten Sie ein Array und eine Reihe von Unterschieden zwischen Arrays