17
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

D言語の更新まとめ 2019年5月版(dmd 2.086.0)

Last updated at Posted at 2019-05-05

はじめに

D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.086.0 が 2019/05/04 にリリースされました。

リリース直前にGCC9.1でD言語サポートが入ったことで一部界隈では盛り上がりを見せていますが、ちゃちゃっと目玉機能抜粋しつつ、ニュースも一緒にまとめていきたいと思います。

なお、バグFixについては現象わかりづらいタイトルも多く、和訳しても役に立つものが少なそうだったので割愛します。

また今回は日本人Contributorとして @kubo39 さんが結構な数の不具合を修正していただいたようで、特徴的なものは何か解説を書いてもらえるやもしれません。

ChangeLog原文は下記リンクを参照してください。

コンパイラ変更点

  • private import で取り込まれていた宣言の利用に関してDeprecatedからErrorになった
  • 生成される opEquals より alias this による opEquals が優先されるようになった
  • コピーコンストラクタが利用可能になった
  • HexString リテラルが廃止となった
  • 選択importによって取り込まれていた宣言の利用に関してDeprecatedからErrorになった
  • 関数リテラルが参照を返せるようになった
  • コンパイラのメモリ要件を緩和するコンパイラオプションとして -lowmem を追加
  • __traits でプライベートメンバーにアクセス可能になった

DeprecatedからErrorになった機能

昨年3月にDeprecatedとされていたいくつかの機能がErrorになるようになりました。

当時の記事で private import, 選択import, HexString についてはすこーしだけ触れていました。
早いもので、あれから期間としては1年強経ったんですね。皆さん大丈夫でしょうか。

alias thisによる opEquals の優先度変更

構造体Bが構造体Aのalias thisを持っているとき、AにopEqualsが定義されていればそれが呼ばれるようになりました。

公式のサンプルコードが分かりやすいです。

struct A
{
    int a, b;
    bool opEquals(ref A rhs) const
    {
        return a == rhs.a && b == rhs.b;
    }
}

struct B
{
    int n;
    A a;
    alias a this;
}

void main()
{
    B a, b;
    assert(a == b);    // a.a.opEquals(b.a) として動作します
}

これは元々構造体のメンバー毎に比較する動作だったので、この変更によってより高速化される可能性があります。

一方で、 alias this 以外にフィールドがある場合はそれについて考慮されなくなります。

alias this の意図は元々クラスの継承と同じように「それに成り代われる」なので、変更意図自体は自然かと思います。もし使っているところがあれば念のためテストが必要、といったところでしょうか。

新機能 コピーコンストラクタ

今回のリリースの中でも目玉機能の1つです。

コピーコンストラクタというのは、ある構造体のインスタンスを同じ構造体のインスタンスをベースに初期化するとき、その処理を特化した記述にすることで最適化を助ける機能になります。

C++にも同名の機能がありますが、ついにD言語にも同じ機能がやってきました。

これはD言語の改善提案である DIP1018 にて提案されていた機能で、Acceptから2ヶ月を掛けて今回のリリースに至っています。
内容は下記にざっくり記載しますが、細かくはDIPを見ていただければと思います。
https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1018.md

コピーコンストラクタの定義

公式サンプルを抜粋すると、シグネチャとしては以下の2通りがあります。

struct A
{
    this(ref return scope A rhs) {}                        // copy constructor
    this(ref return scope const A rhs, int b = 7) {}       // copy constructor with default parameter
}

ちょっとすぐには覚えられないくらいの修飾子が付いていますが、個々の意味を理解すれば多分見なくても書けるようになります。(そんなに書く機会があるか微妙ですが)

個々の説明は公式のリファレンスが詳しいですがざっくり説明を試みます。
https://dlang.org/spec/function.html#return-scope-parameters

ref return 修飾

return 修飾子は他の言語では見かけない修飾子ですが、これはrefとセットで指定することで「ref returnセマンティクス」という動作を明示する意味があります。

どういうことかというと、関数の戻り値に ref A と参照返しが指定されているとき、その参照の生存期間が「インスタンスと同等(フィールドの参照)」なのか「引数と同等(引数の参照)」なのかを判別するための指定になります。(グローバル変数の参照とかは例外っぽいですが)

要するにこれは戻り値(return value)に使うよ、と明示しているイメージですね。

インスタンスが破棄されているのに参照だけ生き残っている場合はいわゆるダングリングポインタ(無効な参照)でありバグの元となりますが、ref returnの引数を持つ場合は引数と同等であるとわかり、より長い生存期間が許可されることになります。
Rustのライフタイムにも似ていますが、これを明示して制御するのが「ref returnセマンティクス」です。

そしてコピーコンストラクタの場合、一般には「引数である構造体インスタンスからフィールドを流用することで最適化を目指す」方向になりますので、戻り値である構造体インスタンス(自分自身)の生存期間は引数依存です。このあたりに矛盾がないよう ref return と付けることになっているようです。

scope 修飾

scopeというのは言ってしまえばコピーを防止する修飾子です。

scopeという単語の意味の通り、現在のスコープ(今回は関数スコープ)から外部へのコピーが行えなくなります。
利用者からすると、下手にコピーされないことが分かるので気兼ねなく引数に渡すことができるようになりますね。(そこまで気にしないかもしれませんが…)

コピー防止というとコピーコンストラクタと相反している気もしますが、必要なフィールドだけコピーして使え、という意味合いですかね。

Postblitコンストラクタとの違い

D言語には元々 Postblit Constructor というのもあり、コピーコンストラクタからこれを連想する方も多いと思います。

この「Postblit」というのは、「フィールド毎にコピーする処理の後」という意味になります。言ってしまえばコピー後のフック関数ですかね。
こちらの用途としては、一通りコピーしたあとに配列などの参照型を厳密にコピーするようなケースが想定されています。

今回追加されたコピーコンストラクタは、Postblitコンストラクタにおけるフィールド毎コピーそのものを差し替えられる機能であり、完全に上位互換となっています。

そんなわけで今回の強化によりPostblitコンストラクタは新しいコード向けには推奨されなくなりました。

その他Postblitの詳細は以下のリンクを参照ください。
https://dlang.org/spec/struct.html#struct-postblit

constimmutable によるオーバーロード

例によって constimmutable で修飾されている場合毎にオーバーロードが可能です。
また、通常の関数と同様 inout 修飾を使うことで記述を簡略化できます。

厳密に書き分ける場合
struct A
{
    this(ref return scope A another) {}                        // 1 - mutable source, mutable destination
    this(ref return scope immutable A another) {}              // 2 - immutable source, mutable destination
    this(ref return scope A another) immutable {}              // 3 - mutable source, immutable destination
    this(ref return scope immutable A another) immutable {}    // 4 - immutable source, immutable destination
}

void main()
{
    A a;
    immutable A ia;

    A a2 = a;      // calls 1
    A a3 = ia;     // calls 2
    immutable A a4 = a;     // calls 3
    immutable A a5 = ia;    // calls 4
}
inoutで簡略化する場合
struct A
{
    this(ref return scope inout A rhs) immutable {}
}

void main()
{
    A r1;
    const(A) r2;
    immutable(A) r3;

    // All call the same copy constructor because `inout` acts like a wildcard
    immutable(A) a = r1;
    immutable(A) b = r2;
    immutable(A) c = r3;
}
コピーコンストラクタが明示されない場合に動作するロジック
this(ref return scope inout(S) src) inout
{
    foreach (i, ref inout field; src.tupleof)
        this.tupleof[i] = field;
}

(foreachのパラメーターにinoutって書けたんだ…考えたことなかった…)

関数リテラルにおける参照返しのサポート

関数リテラルの構文が少し拡張されて参照が返せるようになりました。

関数リテラルというと以下のようなものですね。

今までの関数リテラルの例(ラムダ構文)
alias fn = () => a += 2;

関数が参照を返す場合、リテラル上では表現する方法がないため、一度ローカル関数を作ってaliasを持たせるなどのハックが必要でした。

一度普通に参照返し関数を作ってaliasを取るしかなかった
ref int func()        // a static or non-nested function to mimic a `function` literal
{                     // or a non-static nested function to mimic a `delegate` literal
    return a += 2;
}
alias fn = func;

これに対し、今後は以下のような記述が可能になります。

int x = 1;

auto getRefX = function ref () { return x; }    // `=>` を使うショートハンドもあります
auto getRefX = delegate ref () { return x; }
auto getRefX = ref () { return x; }

alias add2 = ref () => x += 2;
add2() += 7;    // add2は参照を返すので左辺値に使えます
assert(x == 10);

ちょーっと見慣れるまで時間かかりそうな位置にref書いてますが、構文拡張なので覚えておいた方がよさそうですね。

-lowmem フラグ

ざっくりメモリ使用量を~75%削減、コンパイル時間が~30%増加、という感じのフラグらしいです。

コンパイラのテストで大規模なものですが、

  • メモリ使用量は1,630MB -> 410MB
  • コンパイル時間は 4.8秒以下 -> 6.3秒

と記載されているので、CIでスポットインスタンス立てている場合は1つ下のサイズにできるかもしれませんね。

中で何が起きているのかわかりませんが、コンパイラ内情にお詳しい方に解説は託したいと思います。

__traitsにおけるプライベートメンバーのアクセス

getMembergetOverloads でプライベートメンバーが取れるようになります。
メタメタなプログラムを書いている場合はこれも一応要テストという感じですね。

不具合報告は以下のBugzillaになります。
https://issues.dlang.org/show_bug.cgi?id=15371

ランタイム変更点

  • 新しいモジュールの追加 core.sync.event

Event構造体

新しいモジュールが増えました。が、追加されたのは Event 構造体1つだけです。(なんという無限に抽象的な名前…)

Event 構造体のメソッドは大きく2つあり、waitで待機でき、setされると解除されて動き出す、という機能があります。
わかる人にはわかる一言説明をしてしまうと「.NET Frameworkの ManualResetEvent」相当です。

クロスプラットフォーム向けのスレッド間シグナル通信、という説明がありますが、実体はPosix環境だとMutexとConditionの合わせ技、Windows環境では専用のAPIがあるのでシステムコール一発という具合です。
これは完全にWindowsユーザーからのリクエストでしょうね。確かに便利です。

公式ドキュメントのサンプルは以下の通りです。
MutexやConditionより気軽に使えそうなので良いのではないでしょうか

import core.sync.event, core.thread, std.file;

struct ProcessFile
{
    ThreadGroup group;
    Event event;
    void[] buffer;

    void doProcess()
    {
        event.wait();
        // process buffer
    }

    void process(string filename)
    {
        event.initialize(true, false);
        group = new ThreadGroup;
        for (int i = 0; i < 10; ++i)
            group.create(&doProcess);

        buffer = std.file.read(filename);
        event.set();
        group.joinAll();
        event.terminate();
    }
}

(しかしなんで構造体なんだろうなぁ…)

標準ライブラリ変更点

  • std.algorithm.comparison.levenshteinDistanceのメモリ使用量改善
    • 不具合修正なので特に言うことはない感じです…
  • std.experimental.allstd へ移動

std.experimental.allstd へ移動

標準ライブラリのモジュールをすべてimportする std.experimental.all が正式版になりました。
今後は import std; と書けば良いことになります。

これも元々昨年3月版で追加されたものが丸1年で正式版になった、という感じですね。
Playgroundで書くときとかは楽でいいので今後はこれで行きましょう。

ちなみにこれを書いてもコンパイル時間は0.5秒くらいしか伸びないらしいです。(本当か…?)
https://dlang.org/changelog/2.086.0.html#std-all

ツール変更点

インストーラー関連

  • バンドルされているLLDリンカを 8.0.0 へ更新
    • Windows向けにバンドルされているやつです。もっとリンクが早くなると良いですね!

dub

  • dub init でカスタムタイプのサポート
  • Windows環境では既定でOPTLINKを使用しなくなります
  • dub runでローカルにないパッケージを自動的にfetchするようになります
  • list-installedコマンドの廃止
  • upgradeや依存性の解決が一度のリクエストで解決されるようになります
    • パフォーマンス改善、更新と衝突したときに起きる誤動作防止になります

カスタムタイプ

まだ試せていませんが、独自に作ったパッケージ名を -t に追加で指定する(dub init -t my-skelton)ことで、サブパッケージとして init-exec があるときそれをスケルトンに使えるようになるらしい?です。(若干訳が怪しい)

最近開発サポートツールを作ったりしているので、最初からそういうのが設定されたプロジェクトをボイラープレートとして簡単に提供できるようになると良いですね。
単体テストやコードカバレッジなど設定して回るのは結構手間なので、こういう方向性のサポートは助かります。

ニュース

GCCにおけるD言語サポート

GCC9がリリースされ、D言語サポートが追加されました。
https://gcc.gnu.org/gcc-9/changes.html

今までもあったGCCバックエンドの gdc というコンパイラが正式に組み込まれた形です。

D言語が久しぶりに色々な方面で浮上するニュースとなりました。
サポートプラットフォームが大幅に増えるはずなので、組み込み等々への活用に期待したいと思います。

なおD言語開発者のWalterいわく、Iain Buclaw氏がほぼ一人でフロントエンドを書いて採用させるための活動を行ったらしいです。これは足を向けて寝られませんね…

また、Redditの同スレッドでは例によってGoやRustと比較される定例行事が行われています。

みんな好きなの使えばええんやで…

DConf2019

例年、D言語の開発者や利用者が集まるDConfというカンファレンスが行われています。

今年もスケジュールやスピーカー、個々のAbstractがほぼ公開されており、もう間もなく開催される時期となりました。
https://dconf.org/2019/index.html

2019年の開催地はイギリスのロンドン、現地時間で5/8-11、日本時間で同日16時(時差は+8時間、現地は朝)からスタートです。
例年YouTubeでライブ中継されるので、興味がある方はぜひ見てみてください。

注目セッションいくつか抜粋しておきます。

  • All Spreadsheets Must Die
    • https://dconf.org/2019/talks/schadek.html
    • やや過激なタイトルですが、「プログラミング言語同士の比較は当然のことだが、それは局所的な話題でありスプレッドシートこそ最高の言語であること、その代替としてD言語を使うためのテクニックを示す」という内容です
    • スピーカーは今回のDConfの主催者である SymmetryInvestments という世界的投資会社のエンジニアです
  • A Pragmatic Approach for Machine Learning in D
    • https://dconf.org/2019/talks/mueller.html
    • 機械学習のための実用的なアプローチ、ということでダンハンビー社内で稼働しているDを使ったシステムの実例、MXNetの利用法や多次元配列や勾配計算、最適化アルゴリズムなどの要素について話す、というゴリゴリ機械学習系の内容です
    • スピーカーは小売業界向けにデータ分析やマーケティングを行っている「ダンハンビー(dunnhumby)」のエンジニアの方です
  • D for a safer Linux Kernel
    • https://dconf.org/2019/talks/militaru.html
    • より安全なLinuxのカーネルドライバをDで書く、というテーマで得られた経験を話す予定だそうです。実際に「virtio_net」というドライバをDに移植されたということで、その知見をまとめたものになるようです。
    • スピーカーはルーマニアのブカレスト工科大学の学生です。Linuxのセキュリティ強化のためにD言語を使った卒論を書いたのだとか。すごいですね…

なお、今年のスポンサーはあの高級車ブランドのメルセデス・ベンツです!

お金以外にも現地で参加者向けに研究所を宿として貸し出しているとか。
これは一気に資本が流れ込んできている気配を感じますね!今後に期待しましょう!

17
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?