はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.088.0
が 2019/09/01 にリリースされました。
今回は着実に非推奨機能を削りつつ設計を改善、またライブラリに注目の機能強化がいくつか行われたリリースとなっています。
個人的な興味も多分に含みますが、ざっくり目立っていたトピックを眺めていきたいと思います。
より細かい内容は下記リンクからChangeLogや公式ブログの原文をご覧ください。
- ChangeLog : https://dlang.org/changelog/2.088.0.html
- 公式ブログ : https://dlang.org/blog/2019/09/06/dmd-2-088-0-released/
変更点目次
コンパイラ変更点
- D1形式の演算子オーバーロードが非推奨になりました。
-
static
ブロックでのコンストラクタ宣言が非推奨になりました。 -
deprecated
属性はalias
の先においても有効になります。 -
immutable
なフィールドをコンストラクタで2度初期化するのは Obsolete となりました。 -
__traits(getLocation, symbol)
が追加されました。 - 構造体とクラスに対する
read-modify-write
操作のエラーを抑制します。 - 匿名共用体(
union
) のメンバーについてPostblit
とdestructor
が呼び出されなくなりました。
ランタイム変更点
-
core.atomic
のmsync
が削除されました。 - 結果を破棄しない
core.atomic.cas
のオーバーロードを追加しました。 -
core.atomic.atomicExchange
を追加しました. - 非POSIXの
CLOCK
列挙型メンバーはcore.sys.posix.time
から削除されました。 - ランタイムの
callStructDtorsDuringGC
オプションが非推奨となりました。 -
core.sys.posix.dlfcn
のdladdr
,dlvsym
,Dl_info
が削除されました。 -
core.exception
のonHiddenFuncError
とHiddenFuncError
が削除されました。 -
core.exception
のsetAssertHandler
が削除されました。 -
core.thread
のFiber.call(bool)
が削除されました。 -
core.sys.linux.sys.netinet.tcp
モジュールが削除されました。 -
core.runtime
のExceptionHandler
を引数に取るRuntime.initialize
,Runtime.terminate
は削除されました。 -
core.stdcpp.string
が追加されました。 -
core.stdcpp.vector
が追加されました。
ライブラリ変更点
-
std.array.Appender
とRefAppender
は.data()
の代わりに.opSlice()
が推奨されます。 -
ErrnoException.errno
がnothrow pure @nogc @safe
になりました。 -
Nullable
のalias get this
は非推奨になりました。 -
std.file.getAvailableDiskSpace
が追加されました。 -
std.json
はtrailing comma
(末尾コンマ)を許容するようになりました。
インストーラー変更点
- バンドルされている
libcurl
がDONT_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
の例を挙げておきます。
以下のような利用方法になります。
shared(int) data = 10;
shared(int) newVal = 100;
// アトミックな操作によって新しいデータを格納し、元のデータを返します。
auto oldVal = atomicExchange(&data, newVal);
assert(oldVal == 10);
assert(data == 100);
ちなみにこれは自力でやると atomicLoad
と cas
を使ってループして書く必要がありました。
非常に実用的でありがたいですね!
強化: C++連携用モジュールの追加
今回 core.stdcpp.string
と core.stdcpp.vector
という2つのモジュールが追加されました!
これはモジュールの名前からお分かりと思いますが、C++における標準ライブラリの std::basic_string
と std::vector
をD言語で実装したものです。
(書くと長いので省略していますが、基本のテンプレートパラメーターは当然のこと、 char_traits
や Allocator
にも対応しています!)
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_ptr
や std::shared_ptr
などのスマートポインタ、STL関連のイテレーター各種が提供されないため、簡単には移行できない範囲があるのは事実です。
次弾として、今年の秋に Symmetry Autumn of Code という報奨金付きの学生向けのD言語強化イベントがあり、そこでSTLに関して同様の強化を行うプロジェクトが進行中です。
OpenCV 3.0からはC APIをメンテナンスから外すなどの動きも見られますが、Dはそういった壁を乗り越えて使えるようになっていくことでしょう!
ライブラリ変更点
変更: Appender や RefAppender は .data()
の代わりに .opSlice()
が推奨されます
ちょっと意外にも思える変更ですが、可変長配列を効率よく組み立てるための std.array.Appender
と std.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.atomic
のmsync
が削除されました。 - 非POSIXの
CLOCK
列挙型メンバーはcore.sys.posix.time
から削除されました。 - ランタイムの
callStructDtorsDuringGC
オプションが非推奨となりました。 -
core.sys.posix.dlfcn
のdladdr
,dlvsym
,Dl_info
が削除されました。 -
core.exception
のonHiddenFuncError
とHiddenFuncError
が削除されました。 -
core.exception
のsetAssertHandler
が削除されました。 -
core.thread
のFiber.call(bool)
が削除されました。 -
core.sys.linux.sys.netinet.tcp
モジュールが削除されました。 -
core.runtime
のExceptionHandler
を引数に取るRuntime.initialize
,Runtime.terminate
は削除されました。 -
Nullable
のalias get this
は非推奨になりました。
言語的に使われていなかった HiddenFuncError
の削除などまったく影響を受けないような機能もあり、個々の詳細は省きますが、目立ったところだけまとめます。
D1形式の演算子オーバーロードの廃止
今回の変更の中でも大きなトピックの1つです。
D言語がv1からv2にメジャーバージョンアップをしたのは7年ほど前の2012年です。ここから今までD1のときに規定されていた演算子オーバーロードの仕様が残っていました。
一例ですが opAdd
や opMul
という形式で加算や乗算を表現していたところ、今は 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を書いてやれば特に問題ありませんので、具体的な移動先は以下を参照してください。
- https://dlang.org/changelog/2.088.0.html#dlfcn-posix
- https://dlang.org/changelog/2.088.0.html#netinet-tcp
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月、期待して待ちたいと思います!