Swift で strerror_r を使って C 言語の errno を 文字列に変換する

Swift プログラミング

C 言語ではお馴染みな errno に格納されたエラーコードを Swift で文字列に変換する方法です。


C 言語で標準ライブラリを使った時にエラーが発生すると errno にエラーコードが格納されることがよくあります。Swift でも C 言語の標準ライブラリが使えるようになっていて、エラーのときには同じように errno でエラーコードを受け取れます。

そして C 言語の標準ライブラリではそんなエラーコードを文字列に変換する関数が用意されているので、同じように Swift でもそれを使って、エラーコードを文字列に変換してみました。

strerror_r 関数を使って errno を文字列に変換する

errno を文字列に変換するには strerrorstrerror_r の2つの関数があります。

前者を使えば簡単にエラーコード文字列化することができるのですけど、共用のメモリー空間を使うらしくマルチスレッドで安全性を担保できないらしいので、今回は少し難しい後者を使ってみることにします。

Swift で C 言語の標準ライブラリを使う

Swift で C 言語のライブラリ使うためには、OS X 環境であれば次のように Darwin.C フレームワークをインポートします。

import Darwin.C

エラーコードを文字列に変換する

そうしたら strerror_r が使える状態になっているので、実際の変換処理を実装していきます。今回は String変換するためのイニシャライザーを追加して実現してみることにします。

やっていることとしては、最初に C 言語ではお馴染みの malloc関数 を使って、変換した文字列を一時的に保存するための場所を作ります。その上で strerror_r関数 を使って、エラーコードから生成した文字列をバッファに格納します。

文字列が取得できた場合に 0 が返されるので、そうしたら自分自身に取得した文字列を設定して変換完了です。文字変換に失敗した場合は、今回はエラーコードをそのまま文字列にして返します。

extension String {
	
	public init(cPOSIXError: Int32) {
		
		let bufferSize = 512
		let buffer = UnsafeMutablePointer<Int8>(malloc(bufferSize))
		
		defer {
			
			free(buffer)
		}
		
		if strerror_r(cPOSIXError, buffer, bufferSize) == 0 {
			
			self = String.fromCString(buffer)!
		}
		else {

			self = "\(cPOSIXError)"
		}
	}
}

これで、次のようにしてエラーコードを文字列に変換できるようになりました。

let message = String(cPOSIXError: errno)

文字列に変換できないケース

こんな風にしてエラーコードを文字列化するコードを書いてみましたが、ここで使った strerror_r がエラーコードを文字列に変換できない場面というのに、少しだけ癖があるようでした。

たとえば、少なくとも今回試した環境では、エラーコードの範囲が 1 から ELAST までの間の値を渡したときには 変換できた としてくれる様子でした。それよりも大きい値が指定されると strerror_r変換できなかった とするようです。

また、エラーコードに 0 を指定した場合も 変換できた とされて、その時には Undefined error: 0 という文字列が得られました。さらにそれより小さい値を指定すると、それは 変換できなかった とする様子です。