D 1.0   D 2.0
About Japanese Translation

Last update Tue Nov 9 13:50:04 2010

モジュール

Module:
	ModuleDeclaration DeclDefs
	DeclDefs

DeclDefs:
	DeclDef
	DeclDef DeclDefs

DeclDef:
	AttributeSpecifier
	ImportDeclaration
	EnumDeclaration
	ClassDeclaration
	InterfaceDeclaration
	AggregateDeclaration
	Declaration
	Constructor
	Destructor
	UnitTest
	StaticConstructor
	StaticDestructor
	ConditionalDeclaration
	StaticAssert
	TemplateDeclaration
	TemplateMixin
	MixinDeclaration
	;

モジュールはソースファイルと1対1に対応します。 モジュール名は、 ソースファイル名からパスと拡張子を除いたものになります。

モジュールは自動で、その中身をスコープとする名前空間となります。 モジュールは表面的にはクラスと似たところがありますが、別のものです:

モジュールは、package と呼ばれる階層にまとめることができます。

Modules offer several guarantees:

モジュール宣言

ModuleDeclaration では、モジュールの名前と、 属するパッケージを指定します。もし宣言が存在しなければ、 ソースファイル名と同じ名前(パスと拡張子は除かれる)が使用されます。

ModuleDeclaration:
	module ModuleFullyQualifiedName ;

ModuleFullyQualifiedName:
	ModuleName
	Packages . ModuleName

ModuleName:
	Identifier

Packages:
	PackageName
	Packages . PackageName

PackageName:
	Identifier

右端の一個前にある Identifier は、 モジュールが属するパッケージを指します。 パッケージはソースファイルの置かれているディレクトリに対応します。 予約語をパッケージ名にすることはできません。 従って、予約語を対応するディレクトリ名に使うこともできません。

もし ModuleDeclaration を書くならば、 ソースファイルの先頭に、一個だけ書く必要があります。

例:

module c.stdio;    // これはモジュール stdio で、パッケージ c に属します

慣習として、パッケージ名とモジュール名は全て小文字で書きます。 この名前はOSのファイル名やディレクトリ名に対応していて、 多くのファイルシステムでは大文字小文字を区別しないからです。 全て小文字にすると決めておくことで、 プロジェクトを別のファイルシステムへ持っていったときの問題が少なくなります。

import宣言

テキストをそのまま#includeするのではなく、Dでは、import宣言によって シンボルだけをロードします:

ImportDeclaration:
	import ImportList ;
	static import ImportList ;

ImportList:
	Import
	ImportBindings
	Import , ImportList

Import:
	ModuleFullyQualifiedName
	ModuleAliasIdentifier = ModuleFullyQualifiedName

ImportBindings:
	Import : ImportBindList

ImportBindList:
	ImportBind
	ImportBind , ImportBindList

ImportBind:
	Identifier
	Identifier = Identifier

ModuleAliasIdentifier:
	Identifier

import宣言には、一般化されたものからより粒度を細かくしたものまで、 いくつか種類があります。

import 宣言の順番は特に意味を持ちません。

import 宣言の ModuleFullyQualifiedName は、 どのパッケージに含まれているかにかかわらず、 完全修飾が必要です。 import元モジュールからの相対パスの考慮などはなされません。

基本import

一番シンプルなimport文は、 単にimportしたいモジュールを列挙します:

import std.stdio;  // モジュール stdiostd パッケージからimport
import foo, bar;   // モジュール foobar をimport

void main()
{
    writefln("hello!\n");  // std.stdio.writefln を呼び出す
}

名前がカレントの名前空間から見つからなかった場合に、 import したモジュールから名前が探索されます。 import中で見つかった名前が唯一だった場合は、それが採用されます。 複数見つかった場合はエラーになります。

module A;
void foo();
void bar();
module B;
void foo();
void bar();
module C;
import A;
void foo();
void test()
{ foo(); // C.foo() の呼び出し。importよりもカレント名前空間を先に探索する
  bar(); // A.bar() の呼び出し。importが使われる
}
module D;
import A;
import B;
void test()
{ foo();   // エラー。A.foo() か B.foo() か曖昧
  A.foo(); // ok。A.foo()
  B.foo(); // ok。B.foo()
}
module E;
import A;
import B;
alias B.foo foo;
void test()
{ foo();   // B.foo() の呼び出し
  A.foo(); // A.foo() の呼び出し
  B.foo(); // B.foo() の呼び出し
} 

public import

デフォルトでは、import は private です。これは例えば モジュール A がモジュール B を、モジュール B はモジュール C をimportしていた場合、Cの名前はAでは探索されないということです。ただし、 import を特別に public と宣言すると、その import が取り込むモジュール内の全ての名前が、 現在のモジュールを外からimportするモジュールにも見えるようになります。

module A;
void foo() { }
module B;
void bar() { }
module C;
import A;
public import B;
...
foo();	// A.foo() の呼び出し
bar();	// B.bar() の呼び出し
module D;
import C;
...
foo();	// エラー。foo() は未定義
bar();	// ok。B.bar() の呼び出し

static import

基本importは、比較的少なめのモジュールとimportで成り立っているプログラムでは うまく働きます。しかしimportの数が増えてくると、 多数のモジュールをimportしたことによる名前の衝突が起こり始めます。 これを避ける手段のひとつが、static importです。 static import した名前は、 使うときには完全修飾しなければいけません:

static import std.stdio;

void main()
{
    writefln("hello!");            // エラー。writefln は未定義
    std.stdio.writefln("hello!");  // ok。writefln は完全修飾されている
}

改名 import

importするモジュールにローカルな名前を与えて、 その名前で修飾したアクセスを 強制することができます:

import io = std.stdio;

void main()
{
    io.writefln("hello!");         // ok。std.stdio.writefln の呼び出し
    std.stdio.writefln("hello!");  // エラー。std は未定義
    writefln("hello!");            // エラー。writefln は未定義
}

改名importは、 とても長いモジュール名を扱うのに便利です。

選択 import

モジュールから一部のシンボルだけをimportして、 現在の名前空間で束縛することができます:

import std.stdio : writefln, foo = writef;

void main()
{
    std.stdio.writefln("hello!");  // エラー。std は未定義
    writefln("hello!");            // ok。writefln はカレントの名前空間で束縛されている
    writef("world");               // エラー。writef は未定義
    foo("world");                  // ok。std.stdio.writef() の呼び出し
    fwritefln(stdout, "abc");      // エラー。fwritefln は未定義
}

static は選択importに対しては指定できません。

改名選択import

改名importと選択importを組み合わせた例です:

import io = std.stdio : foo = writefln;

void main()
{
    writefln("bar");           // エラー。writefln は未定義
    std.stdio.foo("bar");      // エラー。foo は現在の名前空間で束縛されている
    std.stdio.writefln("bar"); // エラー。std は未定義
    foo("bar");                // ok。foo は現在の名前空間にある
                               // 完全修飾名は不要
    io.writefln("bar");        // ok。io=std.stdio で、ioという名前が
                               // モジュール全体を指すよう導入されている
    io.foo("bar");             // エラー。foo は現在の名前空間にある
                               // io のメンバではない

モジュールのスコープ解決演算子

ローカルの名前で隠されてしまったグローバルの識別子にアクセスしたいこと、 たまにあります。 そんな時は、Dではスコープ解決演算子 '.' を使います:
int x;

int foo(int x)
{
    if (y)
	return x;		// グローバルの x ではなく foo.x を返す
    else
	return .x;		// グローバルの x を返す
}
名前の前に'.'を付けると、モジュールスコープレベルで名前を探索することを意味します。

静的コンストラクタ・デストラクタ

静的コンストラクタ、とは、main() 関数を呼び出すより前に モジュールやクラスの初期化のために実行されるコードのことです。 静的デストラクタは、 逆にmain()の後に、モジュールが確保した システムリソースの解放などのために実行されるコードです。

一つのモジュールに複数の静的コンストラクタや静的デストラクタを定義することもできます。 静的コンストラクタはソースコードに書かれた順に上から実行され、 静的デストラクタはその逆順に実行されます。

静的コンストラクタの順序

静的初期化の順序は、各モジュールに書かれた import によって、暗黙のうちに決定されます。各モジュールは import している他のモジュールに依存していると仮定し、 被依存モジュールを先に構築するように順序が決まります。 このルールに従ってさえいれば、 その他のモジュールどうしの実行順序については特に定まっていません。

import宣言の循環(モジュールがお互いをimportしあう、循環依存)は、 どちらか一方が静的構築の不要なモジュールであれば、問題ありません。 双方とも静的構築が必要であった場合は、 実行時例外が発生します。

モジュール内部での静的構築の順序

モジュールの内部では、静的コンストラクタは、 ソースコードに書かれた順で上から実行されていきます。

静的デストラクタの順序

静的コンストラクタのちょうど逆の順番で実行される、 と定義されています。 モジュールの静的デストラクタコードは、 対応する静的コンストラクタが正しく完了していた場合にのみ実行されます。

単体テストの順序

モジュールの内部では、単体テストは、 ソースコードに書かれた順で上から実行されていきます。

mixin 宣言

MixinDeclaration:
    mixin ( AssignExpression ) ;

AssignExpression は、 コンパイル時定数へと評価される文字列である必要があります。 その文字列の内容は正当な DeclDefs としてコンパイルできるものでなければならず、その場合、その通りにコンパイルされます。