この記事は DMD 2.078.0 Has Been Released – The D Blog を自分用に翻訳したものを 許可を得て 公開するものである。
ソース中にコメントの形で原文を残している。 誤字や誤訳など気になったら Pull requestを投げつけて くれると喜ぶ。
DMDのメジャーリリース、2.078.0が新年にパッケージされ、提供されていました。 完全なチェンジログはdlang.orgで見ることができ、あなたのプラットフォーム向けのコンパイラがメインダウンロードページか2.078.0 リリースディレクトリからダウンロードできます。
このリリースでは複数のQoLの改善、軽微な問題や矛盾の修正が提供され、そのうち3つはDRuntimeを使わずにDでプログラミングをする際の問題を解決するためのものです。
Cランタイムのコンストラクションとデストラクション
Dには静的コンストラクタ/デストラクタがあり、大抵aggregate typeのメンバとしてや、モジュールレベルに存在します。
コンストラクタはDRuntimeの初期化ルーチンの間にレキシカルオーダーで呼ばれ、デストラクタはランタイムが終了する時に逆順に呼ばれます。
しかし-betterC
コンパイラスイッチを使うなどしてDRuntimeのない環境でプログラミングをする時や、ランタイムが途中でなくなった時には、静的コンストラクションとデストラクションができなくなります。
DMD 2.078.0はそれらの環境に静的モジュールコンストラクションとデストラクションを、それぞれpragma(crt_constructor)
、pragma(crt_destructor)
という新しい2つのプラグマの形で導入します。
前者は関数をCのmain
よりも前に、後者はCのmain
よりも後に実行させます。たとえばこのように:
crun1.d
// コンパイル方法: dmd crun1.d
// または: dmd -betterC crun1.d
import core.stdc.stdio;
// 以下すべての関数はC リンケージ(cdecl)
// になります。
extern(C):
pragma(crt_constructor)
void init()
{
puts("init");
}
pragma(crt_destructor)
void fini()
{
puts("fini");
}
void main()
{
puts("C main");
}
新しいプラグマのついた関数はextern(C)
リンケージ属性で宣言される必要があります。
この例ではmain
も、必須ではありませんがextern(C)
として宣言されています。
8行目のコロンによってそれ以降、モジュールの終わりか新しいリンケージ属性が適用されるまでのすべての関数に属性が適用されます。
通常のDのプログラムでは、Cのmain
はDRuntimeのエントリポイントでありコンパイラによって生成されます。
CのランタイムがCのmain
を呼ぶと、DのランタイムはGCの開始、静的コンストラクタの実行、コマンドライン引数の文字列配列化、アプリケーションのmain
関数、つまりDのmain
の呼び出しなどの初期化を行います。
Dのモジュールのmain
にextern(C)
がついている時はコンパイラがCのmain
関数を生成しないため、DRuntimeの実装が根本的に置き換えられます。
-betterC
がコマンドラインに与えられていない、または置き換え実装が提供されていない場合、DRuntimeは利用可能で手動で初期化/終了できます。
上の例は明らかにcrt_constructor
プラグマがinit
の実行をCのmain
よりも前に発生させ、crt_destructor
がfini
を後に実行させることを示しています。
これによってDRuntimeが使えない時の新たな選択肢が導入されます。
しかし、main
からextern(C)
を取り除き、同じコマンドラインを実行すると:
crun2.d
// コンパイル方法: dmd crun2.d
import core.stdc.stdio;
pragma(crt_constructor)
extern(C) void init()
{
puts("init");
}
pragma(crt_destructor)
extern(C) void fini()
{
puts("fini");
}
void main()
{
import std.stdio : writeln;
writeln("D main");
}
Cのmain
はDRuntimeに属し、mainはDのmain
であるという点が異なります。
実行順はinit
、Cのmain
、Dのmain
、fini
となります。
つまり事実上init
はDRuntimeが初期化される前に、fini
は終了した後に呼ばれます。
この例はDRuntimeの関数writeln
を使うため、-betterC
ではコンパイルできません。
writeln
をモジュールの1番上でインポートし、puts
と置き換えると動作することに気づいたかもしれません。
しかしDRuntimeが使えたとしても、crt_constructor
やcrt_destructor
が実行されるときは有効ではないことを忘れないでください。
-betterC
のRAII
-betterC
モードの制限のひとつにRAIIの欠如がありました。
通常のDのコードでは、struct
のデストラクタはインスタンスがスコープから出た時に実行されます。
これはDRuntimeによって行われ、そして-betterC
モードではそのランタイムが使えないため、struct
のデストラクタもまた使えません。
DMD 2.078.0で、それは過去の話になります。
destruct.d
// コンパイル方法: dmd -betterC destruct.d
import core.stdc.stdio : puts;
struct DestroyMe
{
~this()
{
puts("Destruction complete.");
}
}
extern(C) void main()
{
DestroyMe d;
}
面白いことに、これはtry..finally
の文脈で実装されており、そのため副作用として-betterC
モードがtry
、finally
ブロックをサポートするようになりました:
cleanup1.d
// コンパイル方法: dmd -betterC cleanup1.d
import core.stdc.stdlib,
core.stdc.stdio;
extern(C) void main()
{
int* ints;
try
{
// ここでリソースを取得
ints = cast(int*)malloc(int.sizeof * 10);
puts("Allocated!");
}
finally
{
// ここでリソースを開放
free(ints);
puts("Freed!");
}
}
Dのscope(exit)
機能もtry..finally
の文脈で実装されているため、これも-betterC
モードで使うことができます:
cleanup2.d
// コンパイル方法: dmd -betterC cleanup2.d
import core.stdc.stdlib,
core.stdc.stdio;
extern(C) void main()
{
auto ints1 = cast(int*)malloc(int.sizeof * 10);
scope(exit)
{
puts("Freeing ints1!");
free(ints1);
}
auto ints2 = cast(int*)malloc(int.sizeof * 10);
scope(exit)
{
puts("Freeing ints2!");
free(ints2);
}
}
例外が-betterC
モードで実装されていないため、catch
、scope(success)
、scope(failure)
はありません。
選択的ModuleInfo
DRuntimeに依存している一見あいまいな機能にModuleInfo
型があります。
これはリフレクションを実現するために背後で働く型で、ほとんどのDプログラマは聞いたこともありません。
ランタイムをなくそうとする人を除いて。
コンパイラがプログラムのモジュールごとにそのインスタンスを生成するため、ModuleInfo
型の不足についてリンカエラーがいきなり発生することがあります。
DMD 2.078.0はそれを変更します。
コンパイラはコンパイル時のランタイム実装の存在を認めることで、現在の実装がModuleInfo
の宣言を提供しているかどうかを確認できます。
宣言されている場合、インスタンスが適切に生成されます。
宣言されていない場合、インスタンスは生成されません。
これにより、たとえばDでカーネルを書くなどする際にランタイムをなくすことが簡単になります。
その他の変更
WindowsでのDMDの新しいユーザは64ビットの環境をセットアップするのが簡単になりました。
まだMicrosoft build toolsのインストールは必要ですが、DMDは-m64
または-m32mscoff
をコマンドラインで指定して実行された際に、Microsoft Build ToolsパッケージかVisual Studioがインストールされているか自動検出するようになりました。
以前は自動設定はインストーラのみが行なっており、手動インストールの際は手動で設定する必要がありました。
ユニットテストに対してより粒度の細かいコントロールができるようにDRuntimeが強化されました。
注目すべき点はDの実行ファイルに渡せる--DRT-testmode
フラグです。
現在のデフォルト引数"run-main"
では、存在するユニットテストが全て実行され、それが通った後にmain
が実行されます。
DMD 2.080.0からデフォルトになる予定の"test-or-main"
が渡された場合、ユニットテストが存在すればそれが実行されて、プログラムはその結果のサマリーを出して終了します。
ユニットテストが存在しないならmain
が実行されます。
"test-only"
の場合main
は実行されず、テストが存在すればその結果が要約されます。
2018年に向けて
これは2018年最初のDMDのリリースです。 今後12ヶ月間Dプログラミング言語コミュニティで何が起こるか非常に楽しみです。 D言語財団の皆様、明けましておめでとうございます!