Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

モジュール

Module:
	ModuleDeclaration DeclDefs
	DeclDefs

DeclDefs:
	DeclDef
	DeclDef DeclDefs

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

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

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

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

モジュールは数々の保証を提供します:

モジュール宣言

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

ModuleDeclaration:
	module ModuleFullyQualifiedName ;

ModuleFullyQualifiedName:
	ModuleName
	Packages . ModuleName

ModuleName:
	Identifier

Packages:
	PackageName
	Packages . PackageName

PackageName:
	Identifier

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

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

例:

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

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

import 宣言

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

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

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

ImportDeclaration の順番は特に意味を持ちません。

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

基本 import (Basic 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() の呼び出し
}

公開 import (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() の呼び出し

静的 import (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 (Renamed 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 (Selective 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 のメンバではない

スコープ限定 import (Scoped Import)

import 宣言は任意のスコープで使用できます。例:

void main() {
  import std.stdio;
  writeln("bar");
}

このimportは、そのスコープ内での未解決シンボルを探すのに使われます。 importされたシンボルが、外部のスコープのシンボルを隠すことがあります。

関数スコープでは、import 宣言がソースコード中で現れた箇所より下でのみ、 import されたシンボルが見えるようになります。 言い方を変えると、関数スコープでの import は前方参照できません。

void main() {
  void writeln(string) {}
  void foo() {
    writeln("bar"); // main.writeln を呼ぶ
    import std.stdio;
    writeln("bar"); // std.stdio.writeln を呼ぶ
    void writeln(string) {}
    writeln("bar"); // main.foo.writeln を呼ぶ
  }
  writeln("bar"); // main.writeln を呼ぶ
  std.stdio.writeln("bar");  // エラー。std は未定義
}

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

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

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

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

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

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

静的コンストラクタや静的デストラクタはスレッドローカル記憶域に対して実行され、 スレッドが生成/破棄されるたびに実行されます。

shared静的コンストラクタやshared静的デストラクタはグローバル記憶域に対して実行され、 プログラムの開始時と終了時に、 一度ずつだけ実行されます。

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

各モジュールのshared静的コンストラクタは、 どの静的コンストラクタよりも前に実行されます。

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

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

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

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

静的デストラクタの順序

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

shared静的デストラクタは全ての静的デストラクタより後に実行されます。

単体テストの順序

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

mixin 宣言

MixinDeclaration:
    mixin ( AssignExpression ) ;

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