Help us understand the problem. What is going on with this article?

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

はじめに

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

今回は着実に非推奨機能を削りつつ設計を改善、またライブラリに注目の機能強化がいくつか行われたリリースとなっています。

個人的な興味も多分に含みますが、ざっくり目立っていたトピックを眺めていきたいと思います。

より細かい内容は下記リンクからChangeLogや公式ブログの原文をご覧ください。

変更点目次

コンパイラ変更点

  • D1形式の演算子オーバーロードが非推奨になりました。
  • static ブロックでのコンストラクタ宣言が非推奨になりました。
  • deprecated 属性は alias の先においても有効になります。
  • immutable なフィールドをコンストラクタで2度初期化するのは Obsolete となりました。
  • __traits(getLocation, symbol) が追加されました。
  • 構造体とクラスに対する read-modify-write 操作のエラーを抑制します。
  • 匿名共用体(union) のメンバーについて Postblitdestructor が呼び出されなくなりました。

ランタイム変更点

  • core.atomicmsync が削除されました。
  • 結果を破棄しない core.atomic.cas のオーバーロードを追加しました。
  • core.atomic.atomicExchange を追加しました.
  • 非POSIXの CLOCK 列挙型メンバーは core.sys.posix.time から削除されました。
  • ランタイムの callStructDtorsDuringGC オプションが非推奨となりました。
  • core.sys.posix.dlfcndladdr, dlvsym, Dl_info が削除されました。
  • core.exceptiononHiddenFuncErrorHiddenFuncError が削除されました。
  • core.exceptionsetAssertHandler が削除されました。
  • core.threadFiber.call(bool) が削除されました。
  • core.sys.linux.sys.netinet.tcp モジュールが削除されました。
  • core.runtimeExceptionHandler を引数に取る Runtime.initialize, Runtime.terminate は削除されました。
  • core.stdcpp.string が追加されました。
  • core.stdcpp.vector が追加されました。

ライブラリ変更点

  • std.array.AppenderRefAppender.data() の代わりに .opSlice() が推奨されます。
  • ErrnoException.errnonothrow pure @nogc @safe になりました。
  • Nullablealias get this は非推奨になりました。
  • std.file.getAvailableDiskSpace が追加されました。
  • std.jsontrailing comma (末尾コンマ)を許容するようになりました。

インストーラー変更点

  • バンドルされている libcurlDONT_USE_RECV_BEFORE_SEND_WORKAROUND オプションを付けてビルドされたものになります。

DUB変更点

  • dub のグローバル設定がルートパッケージレベルにあっても対象となります。

注目トピック

言語・コンパイラ変更点

機能追加: __traits(getLocation, symbol) の追加

そのままの機能で、 __traits(getLocation, symbol) でコンパイル時にシンボルの定義位置が分かるようになりました。
黒魔術の香りがしますが、使い道はエラーメッセージの改善など色々ありそうです。

ファイル名、行番号、文字数が tuple!(string, int, int) として得られ、以下のようにして使います。

void main()
{
    int data = 100;

    enum loc = __traits(getLocation, data);

    writeln("filename : ", loc[0]); // ファイル名
    writeln("line : ",     loc[1]); // 行番号
    writeln("col : ",      loc[2]); // 文字数
}

ランタイム変更点

強化: core.atomic における cas オーバーロードと atomicExchange の追加

lock-freeなアルゴリズムを書きやすくする目的で、頻出の利用方法に合わせた cas のオーバーロードと新たな atomicExchange 関数が追加されました。

ロックフリーに興味のある方はぜひぜひ仕様書をお読みくださいというところですが、取っ掛かりとして追加された atomicExchange の例を挙げておきます。

以下のような利用方法になります。

atomicExchangeの例
shared(int) data = 10;
shared(int) newVal = 100;

// アトミックな操作によって新しいデータを格納し、元のデータを返します。
auto oldVal = atomicExchange(&data, newVal);

assert(oldVal == 10);
assert(data == 100);

ちなみにこれは自力でやると atomicLoadcas を使ってループして書く必要がありました。
非常に実用的でありがたいですね!

強化: C++連携用モジュールの追加

今回 core.stdcpp.stringcore.stdcpp.vector という2つのモジュールが追加されました!

これはモジュールの名前からお分かりと思いますが、C++における標準ライブラリの std::basic_stringstd::vector をD言語で実装したものです。
(書くと長いので省略していますが、基本のテンプレートパラメーターは当然のこと、 char_traitsAllocator にも対応しています!)

Dには元々 extern (C++) というリンケージがあり、C++の名前空間やクラスをそのまま移植して利用することができます。

しかしC++向けのライブラリには std::string や std::vector を返す関数がつきものです。
実用上、これをDでどう扱えば良いのかは非常に難しい問題となっていました。

今回の強化によって、C++向けに作られたライブラリが、Cのラッパーを作らずにD言語からそのままリンクでき、オーバーヘッドなしにやり取りができる、ということになります!

強化の背景

この強化が推し進められた背景としては、大量のC++資産を持ついくつかの企業がD言語を本格的に利用し始めたことが影響しています。

資産をDで一から再実装するのは大変だし、Cのラッパーを書くのも不本意だ、しかしライブラリは使いたい、と考えたユーザーがいたため、各種コンパイラと同じABIを備え、規格書通りの操作をするのであれば問題なく相互運用できるだろう、と実装して本当に実現してしまった結果がこれというわけです。
(技術的にはこれに加えてマングリングなどもありますが、ここでは一旦置いておきます)

C++で書かれたライブラリは多くあるわけですが、多くの言語はC言語との連携しか対応していません。C++向けのライブラリを使おうと思った場合、「一部のCっぽいところだけ何とか変換して使う」「Cでラッパーを書いて一通りリンク可能にする」「丸ごと使いたい言語で再実装する」というのが考えられる選択肢でした。

これらに加えてD言語は「C++のヘッダーを全面的に変換可能にし、そのままリンクして使う」という選択肢を現実的な範囲で提供することを目指して実際にやってのけた、ということになります。

とはいえ、未だ std::unique_ptrstd::shared_ptr などのスマートポインタ、STL関連のイテレーター各種が提供されないため、簡単には移行できない範囲があるのは事実です。

次弾として、今年の秋に Symmetry Autumn of Code という報奨金付きの学生向けのD言語強化イベントがあり、そこでSTLに関して同様の強化を行うプロジェクトが進行中です。

OpenCV 3.0からはC APIをメンテナンスから外すなどの動きも見られますが、Dはそういった壁を乗り越えて使えるようになっていくことでしょう!

ライブラリ変更点

変更: Appender や RefAppender は .data() の代わりに .opSlice() が推奨されます

ちょっと意外にも思える変更ですが、可変長配列を効率よく組み立てるための std.array.Appenderstd.array.RefAppender の操作が一部変更になります。

具体的には buf.data() として取得していたデータを buf[] としてスライスを取ることでデータを取り出す方式が推奨されるようになりました。

以下のようなコードになります。

import std.array : appender;

auto buffer = appender!string;

buffer.put("Hello, ");
buffer.put("world!");

assert(buffer[] == "Hello, world!");

ちなみに .data() は今まで通り利用でき、非推奨になったわけでもないので警告もありません。そのまま利用できるのでご安心ください。

加えて .opSlice() が実装されたのは今回のリリースからなので、昔のコンパイラで動作させる必要があればもうしばらく .data() を使っておくほうがよさそうです。

非推奨または廃止される機能まとめ

項目だけ並べると結構な数があります。下記13点です。

  • D1形式の演算子オーバーロードが非推奨になりました。
  • static ブロックでのコンストラクタ宣言が非推奨になりました。
  • immutable なフィールドをコンストラクタで2度初期化するのは Obsolete となりました。
  • core.atomicmsync が削除されました。
  • 非POSIXの CLOCK 列挙型メンバーは core.sys.posix.time から削除されました。
  • ランタイムの callStructDtorsDuringGC オプションが非推奨となりました。
  • core.sys.posix.dlfcndladdr, dlvsym, Dl_info が削除されました。
  • core.exceptiononHiddenFuncErrorHiddenFuncError が削除されました。
  • core.exceptionsetAssertHandler が削除されました。
  • core.threadFiber.call(bool) が削除されました。
  • core.sys.linux.sys.netinet.tcp モジュールが削除されました。
  • core.runtimeExceptionHandler を引数に取る Runtime.initialize, Runtime.terminate は削除されました。
  • Nullablealias get this は非推奨になりました。

言語的に使われていなかった HiddenFuncError の削除などまったく影響を受けないような機能もあり、個々の詳細は省きますが、目立ったところだけまとめます。

D1形式の演算子オーバーロードの廃止

今回の変更の中でも大きなトピックの1つです。

D言語がv1からv2にメジャーバージョンアップをしたのは7年ほど前の2012年です。ここから今までD1のときに規定されていた演算子オーバーロードの仕様が残っていました。

一例ですが opAddopMul という形式で加算や乗算を表現していたところ、今は opBinary(string op, T)(T rhs) if (op == "+") という具合に opBinary 1つで様々な二項演算子をカバーできるようになっています。

単項演算子などの対応まで並べると大変なので、詳細はChangeLogの対応表を参照してください。

なお、旧型式の方法で処理をすると、現在の形式でどう書き換えれば良いのかわかる警告が表示されるようになります。
それらしきメッセージを見たら適宜更新していけば良いかと思われます。

onlineapp.d(15): Deprecation: opNeg is deprecated.  Use opUnary(string op)() if (op == "-") instead.

Posixモジュールの再編

いくつかPosixに準拠しない定義が core.sys.posix に含まれていた問題で、以前から警告が表示されていましたが今回実際に定義が移動したというものです。

適当なimportを書いてやれば特に問題ありませんので、具体的な移動先は以下を参照してください。

Nullable の alias get this は非推奨になりました。

Nullableは、内部的に持っている値に暗黙的に変換できる機能がありました。

以下のようなコードです。

Nullable!int v = 10;
int n = v; // int n = v.get(); と同等であり、値を持たない場合に例外が発生する

これは値を持つものしか処理しない場合や、実行時に例外を投げることを許容することで記述を簡単にするための機能です。しかしこれは健全ではないということで廃止になりました。

これは明示的に .get(); を呼び出すことで修正できます。
また、値を持つかどうかは isNull によってチェックできますので合わせて利用してください。

なおこの alias get this は、リリース2.096以降で削除される予定とのことです。
(コンスタントにリリースが継続したとして2020年末、または2021年1月のリリースですかね)

まとめ

継続的な非推奨のマークに加えて演算子オーバーロードの見直しなど、過去の仕様との決別を鮮明にしています。その一方でC++との連携が目立って強化されるようになりました。

加えて atomicExchange の追加など実用に向けたライブラリの改善も継続的に行われており、実用性重視で前に進む姿勢が非常によくわかるリリースだったのではないかと思われます。

奇数月の1日にリリースされるサイクルは継続中で、2020年のリリース予定までコンスタントに計画されています。

C++連携などで明らかにできることが増えていっているので、しばらく面白みのあるリリースが続きそうです。
次は11月、期待して待ちたいと思います!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away