37017


Xcode 8.3.3で以下のソースを変換してbuildしたところエラーが発生しました。

[code]

    open func drChar(fieldNo : Int32, row : Int32, column : Int32, strBuf : [UInt8]?, useDirectChg : Int32) -> Int32 {
        var retValue : Int32 = 0
        if strBuf == nil {
            retValue = drChar(fieldNo, row, column, nil, useDirectChg)
        } else {
            retValue = drChar(fieldNo, row, column, UnsafeMutablePointer<Int8>(strBuf!), useDirectChg)
        }
        return retValue
    }
    
[/code]

エラーの場所は`UnsafeMutablePointer`で、エラーメッセージは

> 'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to temporarily
> view memory as another layout-compatible type.

です。

どのように修正すればよいのでしょうか？

【追加】すみません。ヘッダー部分は以下です。

[code]

    public func DrChar(_ iFldNo: Int32, _ iRow: Int32, _ iCol: Int32, _ strBuf: UnsafeMutablePointer<Int8>!, _ iDChg: Int32) -> Int32
    
[/code]


CC BY-SA 3.0
2017-08-08T07:18:25.680
37012
2017-08-14T07:41:44.807
2017-08-08T08:55:44.740
24782
24782
question
swift3
swift2.3からswift3.1へのコンバート


Swift
3では、「Swift2まではポインター型の変換が簡単すぎた」という反省のもと、いくつかのイニシャライザが廃止になっており、単純な置き換え用のメソッドが用意されていないこともあって、Migratorでもうまく変換できないことが多いようです。

なお、`UnsafePointer(配列)`や`UnsafeMutablePointer(&配列)`と言う書き方は大変危険なもので、特定の条件で動いているように見えても、ちょっとしたことで全く意図しない動作になってしまうことがあります。たとえコンパイルが通る場合でも、ただちに修正していただいた方が良いでしょう。

* * *

さて、本題ですが、ご質問に掲載されたコードは、

  * 配列(の先頭要素)へのポインターを取り出す
  * 呼び出し先の引数型に合わせてそのデータ型を変換する

と言う2種類のことを`UnsafeMutablePointer<Int8>`のイニシャライザで同時に実現していました。Swift
3では、それぞれ別々に行う必要があるので、少々面倒なことになります。

  * 配列(の先頭要素)へのポインターを取り出す

⇒配列の`withUnsafeMutableBufferPointer(_:)`または`withUnsafeBufferPointer(_:)`を呼び出す

  * 呼び出し先の引数型に合わせてそのポインターのデータ型を変換する

⇒ポインターの`withMemoryRebound(to:capacity:_)`を呼び出す

元の構造をできるだけ保つように書き直すと、こんな感じになります。

[code]

    open func drChar(fieldNo: Int32, row: Int32, column: Int32, strBuf: [UInt8]?, useDirectChg: Int32) -> Int32 {
        var retValue: Int32
        if strBuf == nil {
            retValue = DrChar(fieldNo, row, column, nil, useDirectChg)
        } else {
            var strBuffer = strBuf!
            retValue = strBuffer.withUnsafeMutableBufferPointer {bufferPointer in
                bufferPointer.baseAddress!.withMemoryRebound(to: Int8.self, capacity: strBuffer.count) {
                    DrChar(fieldNo, row, column, $0, useDirectChg)
                }
            }
        }
        return retValue
    }
    
[/code]

なお、最終的に`UnsafeMutablePointer`にしないといけないので、immutableなパラメータの`strBuf`を直接使うことはせず、mutableとなるよう`var`にコピーしています。

もうちょっとだけSwiftっぽく書くとこんな感じになるでしょうか。

[code]

    open func drChar(fieldNo: Int32, row: Int32, column: Int32, strBuf: [UInt8]?, useDirectChg: Int32) -> Int32 {
        var retValue: Int32
        if var strBuffer = strBuf {
            retValue = strBuffer.withUnsafeMutableBufferPointer {bufferPointer in
                bufferPointer.baseAddress!.withMemoryRebound(to: Int8.self, capacity: strBuffer.count) {
                    DrChar(fieldNo, row, column, $0, useDirectChg)
                }
            }
        } else {
            retValue = DrChar(fieldNo, row, column, nil, useDirectChg)
        }
        return retValue
    }
    
[/code]

少々面倒に見えるかもしれませんが、今現在判明しているSwiftやSwift標準ライブラリーの仕様に合わせて危険な使い方は一切しないようになっています。

※「正しくは大文字で呼び出していました」のコメントをいただいたので、「ヘッダー部分は以下です」の内容通り、呼び出される側を`DrChar`に修正しました。  
※`DrChar`が内部で引数のポインターを保存して後々も再利用するような場合には、もっと大規模な修正が必要になります。

* * *

C言語で書かれたライブラリーのSwift側のラッパーでしょうか?最初に書いたような変更の他にも、Swift
3ではポインターの取り扱いに大きな変更がありましたので、そのようなライブラリーを多用していると大変かもしれません。ご質問のコードに直接関係ある事柄はこの回答へのコメントとしてお知らせ下さい。別のコードでうまくいかない部分がある場合には別のご質問としてご投稿くださるようお願いします。

(もうじきXcode9の正式版も例年通りの時期に登場しそうです。Xcode9には、Swift2.xからの移行ツールは含まれませんので、要移行のプロジェクトが他にもあるのでしたら、急がれた方が良いでしょう。)


CC BY-SA 3.0
2017-08-08T10:32:38.540
37017
2017-08-14T07:41:44.807
2020-06-17T08:14:45.997
-1
13972
37012
answer