koodev

Swift - 튜플에 포인터로 접근하기

Programming

상황은 이렇다. GLKMatrix4glUniformMatrix4fv()를 사용하려 한다. 문제는 GLKMatrix4가 Float 배열로 이루어진 게 아니라 16개짜리 튜플로 이루어져 있다는 점이다. 배열로 된 경우 자동으로 C포인터로 변환되어 C API에다 사용할 수 있지만 튜플일 경우 그럴 수가 없다. 참고로 여기를 보면 과거에는 GLKMatrix4의 내부 데이터가 튜플이 아니라 배열이었던 것 같다. 참고로 현재 내가 사용중인 Swift 버전은 3.1 이다.

즉, 아래와 같이 하면 컴파일 에러가 난다는게 문제다.

var trans: GLKMatrix4 = GLKMatrix4Identity
// trans에 조작 blah blah...
glUniformMatrix4fv(self.transformLoc, 1, GLboolean(GL_FALSE), &trans.m)

찾아낸 방법은 ios - pass a 4x4 matrix to glUniformMatrix4fv 이다. 요약하면 우선 튜플을 포인터로 변환하고(withUnsafePointer), 그 포인터를 다시 Float 포인터로 변환하는 것이다(withMemoryRebound).

withUnsafePointer는 입력된 변수를 그 타입의 포인터로 변환하여 클로저로 넘겨준다. 그리고 변환된 포인터는 클로저 밖에서는 쓸 수 없다.

withUnsafePointer(to: &trans.m) {
// 이제 $0로 넘어온 포인터로 뭔가를 조작하자
}

클로저 안으로 넘어온 인자의 타입은 UnsafePointer<(Float, Float,...)> 이다(튜플의 포인터: Pointee가 튜플임). 그런데 사용하려는 API의 입력 타입은 UnsafePointer<Float> 이므로 또 다시 변환해주어야 한다.

어떤 포인터를 다른 Pointee 타입의 포인터로 변환하기 위해 withMemoryRebound 메소드를 사용한다. 접근 가능한 갯수도 지정해 줄 수 있는데 원래는 접근할 갯수(16)를 정확히 넣어주어야 하겠지만 API가 포인터 하나만 쓸 것이므로 1을 주어도 상관없다. 모두 정리하면 아래와 같이 쓰면 동작한다.

withUnsafePointer(to: &trans.m) {
    $0.withMemoryRebound(to: Float.self, capacity: 16) {
        glUniformMatrix4fv(self.transformLoc, 1, GLboolean(GL_FALSE), $0)
    }
}

위와 같이 써도 좋지만 조금 더 직관적인 방법이 있다. 위에서 튜플 데이터에 대한 포인터를 만들기 위해 withUnsafePointer()를 썼는데, 이는 튜플 데이터를 바로 UnsafePointer 형태로 만드는게 불가능해서였다. 그런데 대상 데이터 변수를 UnsafeMutablePointer로 감싸는 것은 가능하다.

UnsafeMutablePointer는 UnsafeRawPointer로 변환할 수 있는데 UnsafeRawPointer는 bindMemory() 메소드를 사용할 수 있다. bindMemory()는 원하는 데이터형(Pointee)의 포인터로 바꾸어준다. 즉, 아래와 같이도 쓸 수 있다. 블록 없이 쓸 수 있어서 개인적으로 이 방식이 더 마음에 든다.

let m = UnsafeMutablePointer(&trans.m)
let p = UnsafeRawPointer(m).bindMemory(to: Float.self, capacity: 16)
glUniformMatrix4fv(self.transformLoc, 1, GLboolean(GL_FALSE), p)

물론 이걸 with절로 묶어서 쓸 수도 있다. 그런데 with절로 묶었을 때 들어오는 포인터(주소)값이 그냥 UnsafeMutablePointer로 감쌌을 경우의 포인터값과 다른 것으로 보아 with절을 쓰면 카피가 일어나는 것 같다.

withUnsafeMutablePointer(to: &trans.m) {
    m in
    let p = UnsafeRawPointer(m).bindMemory(to: Float.self, capacity: 16)
    glUniformMatrix4fv(self.transformLoc, 1, GLboolean(GL_FALSE), p)
}

'Programming' 카테고리의 다른 글

macOS에 emacs ggtags 설치 및 설정  (0) 2017.10.17
Xcode에 assimp 올리기  (0) 2017.06.06
OpenGL로 원 그리기  (1) 2017.05.27
Swift3 - result unused warning 없애기  (0) 2017.05.23
Unsigned Integer to String with Generics  (0) 2017.04.23