Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 15888] ファイルの概要

このコミットは、Go言語のcgoツールに関するドキュメントファイルsrc/cmd/cgo/doc.go内の例を修正するものです。具体的には、C言語の関数呼び出しとGoのエラーハンドリング(特にerrnoの扱い)に関する例が更新されています。

コミット

cmd/cgo: fix the cgo example on multiple assignment context.

Change from atoi to strtol since atoi does not set errno.

R=golang-dev, minux.ma, rsc, bradfitz
CC=golang-dev
https://golang.org/cl/7888047

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/0ffdfe42d169bf54ac60240822bb9032648af4c7

元コミット内容

commit 0ffdfe42d169bf54ac60240822bb9032648af4c7
Author: Miki Tebeka <miki.tebeka@gmail.com>
Date:   Thu Mar 21 20:06:25 2013 -0700

    cmd/cgo: fix the cgo example on multiple assignment context.
    
    Change from atoi to strtol since atoi does not set errno.
    
    R=golang-dev, minux.ma, rsc, bradfitz
    CC=golang-dev
    https://golang.org/cl/7888047
---
 src/cmd/cgo/doc.go | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index 3893f7deb5..efbeae9588 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -70,7 +70,7 @@ assignment context to retrieve both the return value (if any) and the
 C errno variable as an error (use _ to skip the result value if the
 function returns void).  For example:
 
-	n, err := C.atoi("abc")
+	n, err := C.sqrt(-1)
 	_, err := C.voidFunc()
 
 In C, a function argument written as a fixed size array

変更の背景

このコミットは、cgoのドキュメント内のコード例が、cgoの重要な機能であるC言語のerrno変数をGoのエラーとして取得する挙動を適切に示していなかったために行われました。

元の例ではC.atoi("abc")が使用されていましたが、C標準ライブラリのatoi関数は、変換エラーが発生してもerrnoを設定しません。そのため、cgoが提供するerr戻り値(Cのerrnoに対応)が常にゼロとなり、エラーハンドリングの例として不適切でした。

この問題を解決するため、errnoを適切に設定するC関数(この場合はsqrt関数が負の数に対してエラーを発生させる例)に置き換えることで、cgoがCのerrnoをGoのエラーとしてどのように扱うかを示す、より正確で教育的な例を提供することが目的です。

前提知識の解説

cgo

cgoは、GoプログラムからC言語のコードを呼び出すためのGoツールです。これにより、既存のCライブラリをGoプロジェクトで再利用したり、Goでは実装が難しい低レベルの操作を行ったりすることが可能になります。cgoを使用すると、Goのコード内でCの関数や型を直接参照できるようになります。

C言語のerrno

C言語では、多くの標準ライブラリ関数がエラー発生時にグローバル変数errnoにエラーコードを設定します。このerrnoは、通常<errno.h>で定義されており、システムコールやライブラリ関数の失敗理由を示す整数値です。プログラマは関数の戻り値とerrnoを組み合わせてエラーを詳細に診断します。

atoistrtol

  • atoi (ASCII to Integer): 文字列を整数に変換するC標準ライブラリ関数です。しかし、atoiは変換エラーが発生してもerrnoを設定しません。エラーを検出する唯一の方法は、戻り値が0であるかどうかを確認することですが、これは有効な変換結果が0である場合と区別できません。
  • strtol (String to Long): 文字列を長整数に変換するC標準ライブラリ関数です。atoiとは異なり、strtolは変換エラーが発生した場合にerrnoを適切に設定します。また、変換が終了した位置へのポインタを返すため、文字列のどの部分までが変換されたかを知ることができます。これにより、より堅牢なエラーハンドリングが可能です。

Go言語の多値代入とcgoのエラーハンドリング

Go言語は関数の戻り値として複数の値を返す「多値代入」をサポートしています。cgoでは、C言語の関数を呼び出す際に、Goの多値代入の仕組みを利用して、C関数の戻り値とCのerrno変数の値を同時に取得できます。具体的には、C.funcName(...)の呼び出しは、result, err := C.funcName(...)のように記述でき、err変数にはCのerrnoの値がGoのerror型としてラップされて格納されます。これにより、Goの慣用的なエラーハンドリングパターンでCのエラーを扱うことが可能になります。

技術的詳細

このコミットの技術的な核心は、atoi関数がerrnoを設定しないというC言語の特性と、cgoがCのerrnoをGoのエラーとして公開するメカニズムの間の不整合を修正することにあります。

cgoは、C関数がエラーを通知するためにerrnoを使用する場合、Goの呼び出し側でそのerrnoの値をGoのerror型として取得できるようにします。これは、GoのコードがCの関数呼び出しの結果をvalue, err := C.some_c_function()のように受け取ることで実現されます。ここでerrは、Cのerrnoの現在の値に基づいて生成されたGoのエラーオブジェクトです。

元のドキュメントの例では、n, err := C.atoi("abc")が使われていました。文字列"abc"は整数に変換できないため、atoiは0を返します。しかし、C標準のatoi関数は、このような無効な入力に対してerrnoを設定しません。したがって、Go側でerr変数を受け取っても、その値は常にゼロ(エラーなし)となり、cgoerrnoをGoのエラーとしてどのように扱うかという重要な側面を示すことができませんでした。

この問題を解決するため、コミットでは例をn, err := C.sqrt(-1)に変更しています。C標準ライブラリのsqrt関数は、負の数に対して呼び出された場合、定義域エラー(domain error)としてerrnoEDOMに設定します。これにより、cgosqrt(-1)の呼び出しから返されたerrnoの値(EDOM)を捕捉し、それをGoのerr変数に適切にマッピングすることができます。結果として、この新しい例は、cgoがCのerrnoをGoのエラーとしてどのように公開し、Goの多値代入とエラーハンドリングの慣習にどのように適合するかを正確に示しています。

コアとなるコードの変更箇所

--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -70,7 +70,7 @@ assignment context to retrieve both the return value (if any) and the
 C errno variable as an error (use _ to skip the result value if the
 function returns void).  For example:
 
-	n, err := C.atoi("abc")
+	n, err := C.sqrt(-1)
 	_, err := C.voidFunc()
 
 In C, a function argument written as a fixed size array

コアとなるコードの解説

変更はsrc/cmd/cgo/doc.goファイルの73行目で行われています。

  • 変更前:

    n, err := C.atoi("abc")
    

    この行は、Cのatoi関数を呼び出し、その結果とerrnoをGoの変数nerrに代入する例でした。しかし前述の通り、atoiはエラー時にerrnoを設定しないため、errは常にnil(Goではエラーなし)となり、cgoerrnoハンドリングのデモンストレーションとしては不適切でした。

  • 変更後:

    n, err := C.sqrt(-1)
    

    この行は、Cのsqrt関数を呼び出し、負の数-1を渡しています。数学的に負の数の平方根は実数ではないため、sqrt関数はエラーを発生させ、CのerrnoEDOM(Domain error)に設定します。これにより、cgoEDOMの値を捕捉し、それをGoのerr変数に適切にマッピングします。この修正により、ドキュメントの例は、cgoがCのerrnoをGoのエラーとしてどのように扱うかを正確に示し、読者がcgoのエラーハンドリングメカニズムを正しく理解できるようになりました。

関連リンク

参考にした情報源リンク