[インデックス 12585] ファイルの概要
このコミットは、Go言語の公式ドキュメントに「C? Go? Cgo!」というタイトルの記事を追加するものです。この記事は、GoプログラムからC言語のコードを呼び出すためのGoの機能であるcgoについて解説しています。具体的には、cgoの基本的な使い方、GoとCの型変換、文字列の扱い、メモリ管理、そしてcgoパッケージのビルド方法について、具体的なコード例を交えながら説明しています。
コミット
commit 60b98d62087d582dafdda68c2af281c5e204fe03
Author: Francisco Souza <franciscossouza@gmail.com>
Date: Tue Mar 13 09:07:37 2012 +1100
doc: add C? Go? Cgo! article
Originally published on The Go Programming Language Blog, March 17, 2011.
http://blog.golang.org/2011/03/c-go-cgo.html
Update #2547.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5777054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/60b98d62087d582dafdda68c2af281c5e204fe03
元コミット内容
このコミットは、Go言語のドキュメントにcgoに関する新しい記事「C? Go? Cgo!」を追加します。この記事は元々2011年3月17日にGoプログラミング言語の公式ブログで公開されたもので、その内容をGoの公式ドキュメントサイトに統合するものです。
変更の概要は以下の通りです。
doc/articles/c_go_cgo.htmlとして新しいHTMLファイルが追加され、記事の本文が含まれています。doc/docs.htmlとdoc/reference.htmlが更新され、新しい記事へのリンクが追加されました。cgoの動作を示すためのGoのサンプルプログラム(cgo1.go、cgo2.go、cgo3.go、cgo4.go)がdoc/progs/ディレクトリに追加されました。doc/progs/runスクリプトが更新され、これらの新しいサンプルプログラムがビルドプロセスに含まれるようになりました。src/cmd/cgo/doc.go内のcgoドキュメントへのリンクが、ブログ記事から新しいドキュメントサイトのパスに変更されました。
変更の背景
このコミットの主な背景は、Go言語のcgo機能に関する重要な情報源であるブログ記事を、Goの公式ドキュメントサイトに直接統合することです。これにより、ユーザーはcgoに関する情報をより簡単に見つけ、アクセスできるようになります。
Go言語は、システムプログラミング言語としての側面も持ち、既存のC言語ライブラリとの連携がしばしば必要とされます。cgoはそのための公式なメカニズムであり、GoプログラムからC関数を呼び出したり、Cのデータ構造を扱ったりすることを可能にします。しかし、cgoの利用にはGoとCの間のインターフェースの理解、メモリ管理の注意点など、特有の知識が必要です。
このブログ記事は、cgoの基本的な概念と実践的な使用方法を分かりやすく解説しており、多くのGo開発者にとって貴重なリソースでした。このコミットによって、その貴重なコンテンツがGoの公式ドキュメントの一部となり、Goの学習者や開発者がcgoについて学ぶ際の障壁が低減されることが期待されます。また、ドキュメントの一元化は、情報の発見可能性と保守性の向上にも寄与します。
前提知識の解説
このコミットの内容を理解するためには、以下の前提知識があると役立ちます。
- Go言語の基本: Go言語の基本的な構文、パッケージシステム、関数、型、エラーハンドリング(特に
defer文)に関する知識が必要です。 - C言語の基本: C言語の基本的な構文、関数、ポインタ、文字列(ヌル終端文字列)、標準ライブラリ(
stdlib.h、stdio.hなど)に関する知識が必要です。特に、C言語におけるメモリ管理(mallocとfree)の概念は重要です。 cgoの役割:cgoは、GoプログラムとC言語のコードを連携させるためのGoのツールです。Goのソースファイル内に特別なコメントブロックとimport "C"ステートメントを記述することで、Cの関数をGoから呼び出したり、Goの関数をCから呼び出したりすることが可能になります。- Goのビルドシステム:
go buildやgo installコマンドがどのようにGoのソースコードをコンパイルし、実行可能ファイルを生成するかについての基本的な理解があると良いでしょう。cgoを使用するファイルは、これらのコマンドによって自動的にcgoツールが呼び出され、GoとCのコードが適切にリンクされます。 - ポインタと
unsafe.Pointer: Go言語は通常、ポインタ演算を制限していますが、unsafeパッケージのunsafe.Pointer型を使用することで、任意の型のポインタを表現し、ポインタ演算を行うことが可能になります。cgoでは、GoとCの間でメモリを共有する際にunsafe.Pointerが頻繁に利用されます。 - Goのドキュメント構造: Goの公式ドキュメントは、
doc/articles/ディレクトリに記事が、doc/progs/ディレクトリに記事内で参照されるサンプルプログラムが配置されるという慣習があります。このコミットは、この慣習に従って新しい記事とサンプルプログラムを追加しています。
技術的詳細
このコミットで追加される「C? Go? Cgo!」記事は、cgoの技術的な側面を以下の点で詳細に解説しています。
-
import "C"疑似パッケージ:cgoを使用するGoのソースファイルでは、import "C"という特殊なインポート文を記述します。- この
Cは実際のGoパッケージではなく、cgoツールによってC言語の名前空間への参照として解釈される「疑似パッケージ」です。 - GoコードからCの関数や変数にアクセスする際は、
C.functionNameやC.variableNameのようにCプレフィックスを使用します。
-
Cヘッダーの埋め込み:
import "C"の直前のコメントブロック(/* ... */)内にC言語のコードを記述できます。- このブロックは、Cコンパイラに渡されるヘッダーとして扱われます。通常、
#includeディレクティブを使用して必要なC標準ライブラリやカスタムヘッダーを含めます。 - このコメントブロックと
import "C"の間には、空行を挟むことはできません。
-
GoとCの型変換:
- GoとCの間でデータをやり取りする際には、明示的な型変換が必要です。
cgoは、Cのプリミティブ型に対応するGoの型を提供します(例:C.long,C.uint)。- Goの型からCの型へ、またはその逆への変換は、通常のGoの型変換構文(例:
int(C.random()),C.uint(i)) を使用して行います。 - 特に、Cの
long型はGoのint型に直接変換できますが、記事では一時変数を使った明示的な変換例も示し、理解を深めています。
-
文字列の扱い:
- C言語にはGoのような組み込みの文字列型がなく、ヌル終端文字配列として表現されます。
cgoは、Goのstring型とCのchar*型を相互に変換するためのヘルパー関数を提供します:C.CString(s string): Goの文字列をCのヌル終端文字列に変換し、Cのヒープにメモリを割り当てます。C.GoString(cs *C.char): Cのヌル終端文字列をGoの文字列に変換します。C.GoStringN(cs *C.char, length C.int): 指定された長さのCの文字列をGoの文字列に変換します。
- これらの変換はデータのコピーを伴います。
-
メモリ管理の注意点:
C.CStringなどのcgo関数によってCのヒープに割り当てられたメモリは、Goのガベージコレクタの管理外です。- したがって、Cのヒープに割り当てられたメモリは、Goコードから明示的に
C.free(unsafe.Pointer(ptr))を呼び出して解放する必要があります。 - 記事では、
defer文を使用してC.freeを遅延実行するイディオムを紹介しており、これによりメモリリークを防ぎつつコードの可読性を保つ方法を示しています。deferは関数の終了時に必ず実行されるため、エラーパスでもメモリが解放されることを保証します。
-
ビルドプロセス:
cgoを使用するGoパッケージは、通常のgo buildやgo installコマンドでビルドできます。goツールはimport "C"を認識し、自動的にcgoツールを呼び出して、GoとCのコードをコンパイルし、リンクします。
これらの技術的詳細は、GoとCの間のインターフェースを効果的に利用し、潜在的な問題を回避するために不可欠な知識です。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、新しい記事ファイルとそれに関連するサンプルプログラムの追加、そして既存のドキュメントからのリンク更新です。
-
doc/articles/c_go_cgo.html(新規追加):- このファイルが、
cgoに関する記事の本文を含んでいます。HTML形式で記述されており、Goのドキュメントサイトのテンプレートシステムと互換性があります。 - 記事内では、
{{code "/doc/progs/cgo1.go" ...}}のようなGoのドキュメントシステム特有の構文が使用されており、これにより外部のGoコード例を記事内に埋め込んで表示しています。
- このファイルが、
-
doc/progs/cgo1.go,doc/progs/cgo2.go,doc/progs/cgo3.go,doc/progs/cgo4.go(新規追加):- これらは、記事内で
cgoの機能(C関数の呼び出し、型変換、文字列処理、メモリ管理)を実演するためのGoのサンプルプログラムです。 - 各ファイルは、
import "C"とCのヘッダーを埋め込むコメントブロックを含んでいます。
- これらは、記事内で
-
doc/docs.htmlおよびdoc/reference.html(変更):- これらのファイルは、Goのドキュメントサイトの主要なインデックスページです。
- 新しい記事
doc/articles/c_go_cgo.htmlへのリンクが追加され、ユーザーが記事を発見しやすくなりました。
-
doc/progs/run(変更):- このシェルスクリプトは、
doc/progsディレクトリ内のGoのサンプルプログラムをビルドおよびテストするために使用されます。 - 新しく追加された
cgoのサンプルプログラム(cgo1、cgo2、cgo3、cgo4)がall変数に追加され、スクリプト実行時にこれらのプログラムもビルドされるようになりました。
- このシェルスクリプトは、
-
src/cmd/cgo/doc.go(変更):cgoコマンド自体のドキュメントファイルです。- 以前はブログ記事へのリンク(
http://blog.golang.org/2011/03/c-go-cgo.html)が記載されていましたが、このコミットにより、新しく追加された公式ドキュメント内の記事へのパス(http://golang.org/doc/articles/c_go_cgo.html)に更新されました。これにより、ドキュメントの一貫性が保たれます。
これらの変更は、Goのドキュメントエコシステムにcgoに関する包括的なリソースを統合し、ユーザーエクスペリエンスを向上させることを目的としています。
コアとなるコードの解説
ここでは、追加されたサンプルプログラムの中から、cgoの主要な概念を示すcgo1.goとcgo3.goのコードを解説します。
doc/progs/cgo1.go の解説
このファイルは、C標準ライブラリのrandom()とsrandom()関数をGoから呼び出す方法を示しています。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rand
// INCLUDE OMIT
/*
#include <stdlib.h>
*/
import "C"
// STOP OMIT
func Random() int {
return int(C.random())
}
// STOP OMIT
func Seed(i int) {
C.srandom(C.uint(i))
}
// END OMIT
package rand: このファイルがrandパッケージの一部であることを示します。/* #include <stdlib.h> */: このコメントブロックは、Cコンパイラに渡されるCコードです。ここでは、random()とsrandom()関数が定義されているC標準ライブラリのstdlib.hをインクルードしています。このブロックはimport "C"の直前に、空行なしで記述する必要があります。import "C":cgoを使用することを示す特別なインポート文です。これにより、Goコード内でC疑似パッケージを通じてCの関数や型にアクセスできるようになります。func Random() int:C.random(): Cのrandom()関数を呼び出しています。CプレフィックスがCの名前空間へのアクセスを示します。C.random()はCのlong型を返しますが、Goのint型に変換するためにint()でキャストしています。
func Seed(i int):C.srandom(C.uint(i)): Cのsrandom()関数を呼び出しています。srandom()はCのunsigned int型の引数を期待するため、Goのint型の引数iをC.uint(i)としてCのunsigned int型に変換しています。
この例は、GoからCの関数を呼び出し、GoとCの間でプリミティブ型を変換する基本的なパターンを示しています。
doc/progs/cgo3.go の解説
このファイルは、Goの文字列をCの文字列に変換し、Cのfputs()関数を使って標準出力に書き込む方法、そしてCで割り当てられたメモリを解放する方法を示しています。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package print
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import "unsafe"
func Print(s string) {
cs := C.CString(s)
C.fputs(cs, (*C.FILE)(C.stdout))
C.free(unsafe.Pointer(cs))
}
// END OMIT
package print: このファイルがprintパッケージの一部であることを示します。#include <stdio.h>と#include <stdlib.h>: Cのfputs()関数が定義されているstdio.hと、C.CStringが内部的に使用するメモリ割り当て(malloc)とC.freeが定義されているstdlib.hをインクルードしています。import "unsafe":C.freeに渡すために、Cのポインタをunsafe.Pointerに変換する必要があるため、unsafeパッケージをインポートしています。func Print(s string):cs := C.CString(s): Goのstring型の引数sを、Cのヌル終端文字列(*C.char)に変換します。この関数はCのヒープに新しいメモリを割り当て、Goの文字列の内容をコピーします。C.fputs(cs, (*C.FILE)(C.stdout)): Cのfputs()関数を呼び出し、変換されたC文字列csを標準出力(C.stdout)に書き込みます。C.stdoutはCのFILE*型ですが、Go側では*C.FILEとして扱われます。C.free(unsafe.Pointer(cs)): 非常に重要です。C.CStringによってCのヒープに割り当てられたメモリは、Goのガベージコレクタの管理外です。そのため、使用後は明示的にC.free()を呼び出してメモリを解放する必要があります。C.freeはvoid*型の引数を取るため、*C.char型のcsをunsafe.Pointerにキャストして渡しています。
この例は、GoとCの間で文字列を安全にやり取りし、Cで割り当てられたリソースを適切に管理する方法を示しています。特に、C.freeの呼び出しを忘れるとメモリリークにつながるため、このパターンはcgoプログラミングにおいて非常に重要です。記事では、このC.freeの呼び出しをdefer文で囲むことで、より堅牢なコードを書く方法もcgo4.goで示しています。
関連リンク
- Go言語公式ブログの元記事: http://blog.golang.org/2011/03/c-go-cgo.html
cgoコマンドのドキュメント: https://golang.org/cmd/cgo/unsafeパッケージのドキュメント: https://golang.org/pkg/unsafe/- Goの
defer、panic、recoverに関する記事: https://golang.org/doc/articles/defer_panic_recover.html (記事内でdeferのイディオムを説明する際に参照されています) - Russ Cox氏の
gosqliteプロジェクト: http://code.google.com/p/gosqlite/source/browse/sqlite/sqlite.go (cgoベースのパッケージのシンプルな例として記事で紹介されています) - Go Project Dashboardの
cgoタグ付きプロジェクト: https://godashboard.appspot.com/project?tag=cgo (記事で他のcgoパッケージの例として紹介されています) - Goランタイムの
cgocall.cソースコード: https://golang.org/src/pkg/runtime/cgocall.c (cgoの内部動作に興味がある場合に参照するよう記事で示されています)
参考にした情報源リンク
- コミット情報:
./commit_data/12585.txt - GitHubコミットページ: https://github.com/golang/go/commit/60b98d62087d582dafdda68c2af281c5e204fe03
- Go言語公式ブログ: https://blog.golang.org/
- Go言語公式ドキュメント: https://golang.org/doc/
cgoに関する一般的な情報源 (Web検索結果に基づく)