$ swift --version
Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36)
Target: x86_64-apple-macosx10.9
Swift is convenient because you can easily call functions in C language. You can also pass Swift's ʻArray
This is achieved by the mechanism that ʻArray is implicitly cast to ʻUnsafePointer <T>
in the function call part.
But when it comes to 2D arrays, it doesn't work. Double pointers are used when receiving a two-dimensional array in C. For example in C
void f0(const int * const * p);
From Swift's point of view, the function defined as
func f0(_ p: UnsafePointer<UnsafePointer<CInt>?>!)
Is interpreted as. On the other hand, if there is let arr: Array <Array <CInt >>
on the Swift side, the following will not work.
let arr: Array<Array<CInt>> = [ [0, 1, 2], [3, 4, 5] ]
f0(arr) //Compile error
Because, what you get by implicitly converting ʻArray <Array
UnsafePointer<Array<CInt>>
And
UnsafePointer<UnsafePointer<CInt>?>
Because it is not.
So, if you look at the transformation that takes ʻUnsafePointer from ʻArray <T>
,
func withUnsafeBufferPointer<R>(_ body: (UnsafeBufferPointer<Array.Element>) throws -> R) rethrows -> R
Can be found. You may want to use it to write code like this: This ** compiles but shouldn't. ** **
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)
This is because the pointer passed to the closure of the withUnsafeBufferPointer
method argument should only be used during the execution of this method.
It's a good use for recursive calls because you have to convert the arrays one by one to pointers, which can only be used during withUnsafeBufferPointer
.
After converting an array with withUnsafeBufferPointer
, the procedure is to build an array of pointers by converting the next array inside the method.
This recursive process is hard to write many times, so let's make it an extension method of ʻArray for reuse, and get ʻArray <UnsafeBufferPointer <T >>
from ʻArray <Array I made
withUnsafeBufferPointerArray`.
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!
}
}
Using an in-function function makes it easy to write a process that combines this kind of state operation and recursive call. I don't like copying the return value as many times as there are elements, so I use ʻOptional and
! `.
Now, with this you can call f0
as follows:
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)
}
As a practical example, OpenGL ES
void glShaderSource(
GLuint shader,
GLsizei count,
const GLchar * const *string,
const GLint *length);
When used to call
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) })
}
}
It's like that. It's nice that the count
, string
, and length
arguments can all be easily constructed from buffers
.
Recommended Posts