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

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

このコミットは、Go言語のcmd/cgoツールに関するドキュメントを更新し、//exportディレクティブを使用する際のC言語のプロローグ(preamble)に関する重要な制約を明確にしています。具体的には、//exportを使用するファイル内のプロローグには、定義(definitions)ではなく宣言(declarations)のみを含める必要があるという点について追記されています。

コミット

commit ed9fc7531d80d0894d52c96a4a79968842df945b
Author: Russ Cox <rsc@golang.org>
Date:   Fri Feb 1 08:33:52 2013 -0800

    cmd/cgo: document //export preamble restriction
    
    Fixes #3497.
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/7235075

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

https://github.com/golang/go/commit/ed9fc7531d80d0894d52c96a4a79968842df945b

元コミット内容

cmd/cgo: //exportプロローグの制約を文書化。 Issue #3497 を修正。

変更の背景

このコミットは、Go言語のcgoツールを使用する開発者が直面していた問題、特に//exportディレクティブとC言語のプロローグの組み合わせに関する混乱やコンパイルエラーを解決するために行われました。コミットメッセージに「Fixes #3497」とあることから、GitHub Issue #3497で報告された問題に対応していることがわかります。

一般的なシナリオとして、cgoを使ってGo関数をCから呼び出せるようにエクスポートする際(//exportを使用)、Goファイル内のimport "C"ブロックの前にC言語のコード(プロローグ)を記述することがあります。このプロローグにCの変数や関数の「定義」を含めてしまうと、cgoが生成する複数のCソースファイル間でシンボルの重複定義が発生し、リンク時にエラーとなる問題が発生していました。

この問題は、cgoが//exportディレクティブを持つGoファイルを処理する際に、プロローグの内容を複数の異なるC出力ファイルにコピーするというcgoの内部的な動作に起因します。ドキュメントがこの制約を明確にしていなかったため、開発者は意図せず重複定義エラーに遭遇し、デバッグに時間を要していました。このコミットは、その混乱を解消し、正しいcgoの利用方法を促すためにドキュメントを更新しました。

前提知識の解説

  • cgo: Go言語とC言語(またはC++、Objective-Cなど)の相互運用を可能にするGoのツールです。GoコードからC関数を呼び出したり、CコードからGo関数を呼び出したりする際に使用されます。cgoは、GoとCの間の呼び出し規約の変換や、型変換などを自動的に処理します。
  • //export ディレクティブ: cgoにおいて、Go言語で書かれた関数をC言語から呼び出せるように「エクスポート」するための特別なコメントディレクティブです。Goの関数定義の直前に//export FunctionNameのように記述します。これにより、cgoは対応するCのヘッダファイルとCのスタブコードを生成し、CコードからGo関数を通常のC関数のように呼び出せるようにします。
  • Preamble (プロローグ): cgoを使用するGoソースファイルにおいて、import "C"の行の直前に記述されるC言語のコードブロックを指します。このブロック内のコードは、cgoによって生成されるCソースファイルにそのままコピーされます。通常、Cのヘッダファイルのインクルード(例: #include <stdio.h>)、Cの関数宣言、構造体や列挙型の定義など、GoコードからCの機能を利用するために必要なC言語の定義や宣言がここに記述されます。
  • Declaration (宣言) と Definition (定義): C言語における非常に重要な概念です。
    • 宣言 (Declaration): 変数や関数の名前、型、引数リストなどをコンパイラに伝えるものです。これは「こういうものが存在しますよ」と教えるだけで、メモリの割り当てや具体的な実装は伴いません。例えば、extern int myVar;(変数の宣言)、void myFunction(int);(関数の宣言)などです。宣言は複数回行うことができます。
    • 定義 (Definition): 変数に実際にメモリを割り当てたり、関数に具体的な処理内容(関数本体)を提供したりするものです。これは「実際に存在する具体的な実体」を指します。例えば、int myVar = 10;(変数の定義)、void myFunction(int x) { /* ... */ }(関数の定義)などです。定義は通常、プログラム全体で一度しか行うことができません(One Definition Rule; ODR)。複数の定義があると、リンカがどの定義を使用すべきか判断できず、「multiple definition of symbol」のようなエラーが発生します。

技術的詳細

このコミットが対処している技術的な問題は、C言語の「宣言」と「定義」の区別、そしてcgoが//exportディレクティブを処理する際の内部的なコード生成メカニズムに深く関連しています。

  1. cgoのコード生成: //exportディレクティブがGoファイルで使用されると、cgoはGo関数をCから呼び出すためのラッパーコードを含むCソースファイルを生成します。重要なのは、この際にcgoが複数のC出力ファイルを生成する可能性があるという点です。具体的には、GoのランタイムとリンクされるCコードと、Goのビルドシステムが使用する別のCコード(例えば、GoのツールチェーンがCコンパイラを呼び出すためのもの)の2つのコンテキストでプロローグの内容が必要になるため、プロローグが複数のCファイルにコピーされることがあります。
  2. 重複定義の問題: もし、//exportを使用するGoファイルのプロローグにCの変数や関数の「定義」が含まれていた場合、その定義はcgoによって生成される複数のC出力ファイルそれぞれにコピーされてしまいます。結果として、コンパイル後のリンク段階で、同じシンボル(変数名や関数名)に対して複数の定義が存在することになり、リンカはどの定義を使用すべきか判断できず、「multiple definition of symbol」というエラーを報告します。これはC/C++のコンパイル・リンクプロセスにおける基本的なルール違反です。
  3. 解決策としてのドキュメント更新: この問題を回避するためには、//exportを使用するファイルのプロローグには、シンボルの「宣言」のみを含め、具体的な「定義」は含めないようにする必要があります。定義が必要な場合は、別のCソースファイル(.cファイル)に記述するか、//exportを使用しないGoファイルのプロローグに記述する必要があります。このコミットは、この重要な制約をsrc/cmd/cgo/doc.goに明示的に追記することで、開発者がこの問題を未然に防げるようにしています。

この変更は、cgoのユーザーがよりスムーズにGoとCの相互運用を行えるようにするための、ドキュメント上の重要な改善です。

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

変更はsrc/cmd/cgo/doc.goファイルに対して行われました。具体的には、以下の5行が追加されています。

--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -116,6 +116,11 @@ copied from the cgo input files. Functions with multiple
 return values are mapped to functions returning a struct.\n Not all Go types can be mapped to C types in a useful way.\n \n+Using //export in a file places a restriction on the preamble:\n+since it is copied into two different C output files, it must not\n+contain any definitions, only declarations. Definitions must be\n+placed in preambles in other files, or in C source files.\n+\n Cgo transforms the input file into four output files: two Go source\n files, a C file for 6c (or 8c or 5c), and a C file for gcc.\n \n```

## コアとなるコードの解説

追加されたコードは、`src/cmd/cgo/doc.go`内のcgoの動作に関する説明セクションに挿入されています。

```go
Using //export in a file places a restriction on the preamble:
since it is copied into two different C output files, it must not
contain any definitions, only declarations. Definitions must be
placed in preambles in other files, or in C source files.

この新しい段落は、以下の点を明確に説明しています。

  1. //export使用時の制約: //exportディレクティブをファイル内で使用する場合、そのファイルのプロローグには制約が課せられること。
  2. 理由: プロローグの内容が「2つの異なるC出力ファイル」にコピーされるためであること。
  3. 具体的な制約: そのため、プロローグには「定義(definitions)」を含めてはならず、「宣言(declarations)」のみを含める必要があること。
  4. 定義の配置場所: 定義が必要な場合は、他のファイル(//exportを使用しないGoファイルのプロローグ、または純粋なCソースファイル)に配置する必要があること。

この説明により、cgoユーザーは//exportとプロローグを安全かつ正しく使用するための重要なガイドラインを得ることができます。これにより、前述の重複定義によるリンクエラーを回避し、開発プロセスをスムーズに進めることが可能になります。

関連リンク

  • GitHub Issue #3497: このコミットが修正した問題の報告元。
  • Gerrit Change 7235075: このコミットに対応するGoプロジェクトのGerritレビューページ。

参考にした情報源リンク