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

[インデックス 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.htmldoc/reference.htmlが更新され、新しい記事へのリンクが追加されました。
  • cgoの動作を示すためのGoのサンプルプログラム(cgo1.gocgo2.gocgo3.gocgo4.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について学ぶ際の障壁が低減されることが期待されます。また、ドキュメントの一元化は、情報の発見可能性と保守性の向上にも寄与します。

前提知識の解説

このコミットの内容を理解するためには、以下の前提知識があると役立ちます。

  1. Go言語の基本: Go言語の基本的な構文、パッケージシステム、関数、型、エラーハンドリング(特にdefer文)に関する知識が必要です。
  2. C言語の基本: C言語の基本的な構文、関数、ポインタ、文字列(ヌル終端文字列)、標準ライブラリ(stdlib.hstdio.hなど)に関する知識が必要です。特に、C言語におけるメモリ管理(mallocfree)の概念は重要です。
  3. cgoの役割: cgoは、GoプログラムとC言語のコードを連携させるためのGoのツールです。Goのソースファイル内に特別なコメントブロックとimport "C"ステートメントを記述することで、Cの関数をGoから呼び出したり、Goの関数をCから呼び出したりすることが可能になります。
  4. Goのビルドシステム: go buildgo installコマンドがどのようにGoのソースコードをコンパイルし、実行可能ファイルを生成するかについての基本的な理解があると良いでしょう。cgoを使用するファイルは、これらのコマンドによって自動的にcgoツールが呼び出され、GoとCのコードが適切にリンクされます。
  5. ポインタとunsafe.Pointer: Go言語は通常、ポインタ演算を制限していますが、unsafeパッケージのunsafe.Pointer型を使用することで、任意の型のポインタを表現し、ポインタ演算を行うことが可能になります。cgoでは、GoとCの間でメモリを共有する際にunsafe.Pointerが頻繁に利用されます。
  6. Goのドキュメント構造: Goの公式ドキュメントは、doc/articles/ディレクトリに記事が、doc/progs/ディレクトリに記事内で参照されるサンプルプログラムが配置されるという慣習があります。このコミットは、この慣習に従って新しい記事とサンプルプログラムを追加しています。

技術的詳細

このコミットで追加される「C? Go? Cgo!」記事は、cgoの技術的な側面を以下の点で詳細に解説しています。

  1. import "C"疑似パッケージ:

    • cgoを使用するGoのソースファイルでは、import "C"という特殊なインポート文を記述します。
    • このCは実際のGoパッケージではなく、cgoツールによってC言語の名前空間への参照として解釈される「疑似パッケージ」です。
    • GoコードからCの関数や変数にアクセスする際は、C.functionNameC.variableNameのようにCプレフィックスを使用します。
  2. Cヘッダーの埋め込み:

    • import "C"の直前のコメントブロック(/* ... */)内にC言語のコードを記述できます。
    • このブロックは、Cコンパイラに渡されるヘッダーとして扱われます。通常、#includeディレクティブを使用して必要なC標準ライブラリやカスタムヘッダーを含めます。
    • このコメントブロックとimport "C"の間には、空行を挟むことはできません。
  3. GoとCの型変換:

    • GoとCの間でデータをやり取りする際には、明示的な型変換が必要です。
    • cgoは、Cのプリミティブ型に対応するGoの型を提供します(例: C.long, C.uint)。
    • Goの型からCの型へ、またはその逆への変換は、通常のGoの型変換構文(例: int(C.random()), C.uint(i)) を使用して行います。
    • 特に、Cのlong型はGoのint型に直接変換できますが、記事では一時変数を使った明示的な変換例も示し、理解を深めています。
  4. 文字列の扱い:

    • 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の文字列に変換します。
    • これらの変換はデータのコピーを伴います。
  5. メモリ管理の注意点:

    • C.CStringなどのcgo関数によってCのヒープに割り当てられたメモリは、Goのガベージコレクタの管理外です。
    • したがって、Cのヒープに割り当てられたメモリは、Goコードから明示的にC.free(unsafe.Pointer(ptr))を呼び出して解放する必要があります。
    • 記事では、defer文を使用してC.freeを遅延実行するイディオムを紹介しており、これによりメモリリークを防ぎつつコードの可読性を保つ方法を示しています。deferは関数の終了時に必ず実行されるため、エラーパスでもメモリが解放されることを保証します。
  6. ビルドプロセス:

    • cgoを使用するGoパッケージは、通常のgo buildgo installコマンドでビルドできます。
    • goツールはimport "C"を認識し、自動的にcgoツールを呼び出して、GoとCのコードをコンパイルし、リンクします。

これらの技術的詳細は、GoとCの間のインターフェースを効果的に利用し、潜在的な問題を回避するために不可欠な知識です。

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

このコミットのコアとなる変更は、新しい記事ファイルとそれに関連するサンプルプログラムの追加、そして既存のドキュメントからのリンク更新です。

  1. doc/articles/c_go_cgo.html (新規追加):

    • このファイルが、cgoに関する記事の本文を含んでいます。HTML形式で記述されており、Goのドキュメントサイトのテンプレートシステムと互換性があります。
    • 記事内では、{{code "/doc/progs/cgo1.go" ...}}のようなGoのドキュメントシステム特有の構文が使用されており、これにより外部のGoコード例を記事内に埋め込んで表示しています。
  2. doc/progs/cgo1.go, doc/progs/cgo2.go, doc/progs/cgo3.go, doc/progs/cgo4.go (新規追加):

    • これらは、記事内でcgoの機能(C関数の呼び出し、型変換、文字列処理、メモリ管理)を実演するためのGoのサンプルプログラムです。
    • 各ファイルは、import "C"とCのヘッダーを埋め込むコメントブロックを含んでいます。
  3. doc/docs.html および doc/reference.html (変更):

    • これらのファイルは、Goのドキュメントサイトの主要なインデックスページです。
    • 新しい記事doc/articles/c_go_cgo.htmlへのリンクが追加され、ユーザーが記事を発見しやすくなりました。
  4. doc/progs/run (変更):

    • このシェルスクリプトは、doc/progsディレクトリ内のGoのサンプルプログラムをビルドおよびテストするために使用されます。
    • 新しく追加されたcgoのサンプルプログラム(cgo1cgo2cgo3cgo4)がall変数に追加され、スクリプト実行時にこれらのプログラムもビルドされるようになりました。
  5. 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.gocgo3.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型の引数iC.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.freevoid*型の引数を取るため、*C.char型のcsunsafe.Pointerにキャストして渡しています。

この例は、GoとCの間で文字列を安全にやり取りし、Cで割り当てられたリソースを適切に管理する方法を示しています。特に、C.freeの呼び出しを忘れるとメモリリークにつながるため、このパターンはcgoプログラミングにおいて非常に重要です。記事では、このC.freeの呼び出しをdefer文で囲むことで、より堅牢なコードを書く方法もcgo4.goで示しています。

関連リンク

参考にした情報源リンク