これは長い投稿記事です。
最近、このブログ記事についてずっと考えていました。これは2019年に初めてC++の代替としてD言語を使用してビデオゲームを開発した作者の経験を記録しています。
別の記事では、D言語を使用することへの不満体験や、なぜコードを非公開の外部言語であるJaiに移植したのかについて議論しています。
しかし、彼らはその言語が非公開の状態にあることがライブラリの利用可能性に与える将来の影響を無視しています。
現在、わずか2年が経過したにもかかわらず、Jaiのコミュニティは熱心ですが依然として公開されておらず、記事で指摘されていたD言語の問題の半分以上は解決されています。これはD言語コミュニティの力を示す良い例です。
最近、新しい言語機能の導入でいくつかの障害に直面しましたが、それでも短い2年間で大きな変化がありました。ここで、作者が当時にD言語を使用していた問題点を振り返り、それらを検討することでD言語の成長を称賛し、言語に対するいくつかの誤解を分析したいと思います。
Windowsで利用可能な2つのD言語コンパイラ(dmdとldc2)の間で約4年間行き来した結果、Windows上でのD言語の状態は趣味プロジェクトに対する私の期待に似ていると感じています。
これまでの経験から見て、現在の最大問題はWindows上でのデバッグ情報が完全に壊れていることです。
誰かが「DMDは常にデバッグ情報に多くの問題があった」と言っていましたが、残念なことに、これらの問題の多くはDMDだけでなく両方のコンパイラに当てはまります。まず、GDCもWindowsで使用可能です(少なくともある程度は)。Windows上のD言語コンパイラの品質について言及することはできません(私は自宅では使用禁止にしているため)、しかし彼らがLDC2の問題に遭遇したと聞いているので、多くのデバッグ問題はおそらくVS、またはそれが使用するデバッガとの相互作用から来ているのではないかと疑っています。ただし、VSCodeか完全なVSIDEのどちらを使用していたかは不明なため、答えは依然として曖昧です。
プラグイン(D言語のマクロに相当するもの)がデバッグ情報を生成すると、デバッダが正しいファイルを見つけられなくなり(したがって逆アセンブルする必要があります)。-mixinフラグはこれを常に解決してくれましたが、なぜWindowsでは機能しないのか理解できません。
その他の問題や欠点があり、その多くはメタプログラミングに関連しています:異なるコンパイラ段階間の奇妙な相互作用が、メタプログラミングで意図しない結果を引き起こし、同時に誤解を招くエラーを生成します。例えば、グローバルスコープでの順序に依存しない宣言は、プラグインと一緒に使用すると壊れることがあります。
これは、ある程度の知識があれば、論理的にその振る舞いを認識できるという意味で予期されることです。コンパイラは神ではありません。
一般的にD言語は前方宣言を必要としませんが、プラグインが独自のパスを提供しない場合、 naturallyは順序解析されたコードを生成します。
D言語のバックエンド設計はより短いコンパイル時間を好み、追加のパスを追加すると時間に大きな悪影響があります。
しかし、D言語コンパイラは確かにエラーメッセージに問題があります。この記事が書かれて以来、以前は奇妙だったエラーメッセージ(特にセミコロンと右括弧の欠如)は改善されましたが、コンパイラがエイリアスインスタンス化されたテンプレート内のCTFEのラムダの7層のネストされたプラグインでエッジケース(あるいはバグ)に遭遇すると、常に奇妙なエラーメッセージを受け取ることになります。
名前空間名のように、特殊な種類の文字列リテラルを使用しない限り、プラグイン内では解決されません。これは非常に奇妙な問題です。まず、D言語は「名前空間」を使用せず、「スコープ」を使用します。完全修飾識別子を想定していますが、プラグインまたはトークン文字列のD言語仕様の歴史ではそのような証拠を見つけることができません。
あるいは、彼らはC++相互運用性について言及していたため、D言語のextern(C++)の「名前空間」変数を使用していたのかもしれません(私は推奨しません、代わりに文字列バージョンを使用してください)。これらのいずれかかがかつて実装の詳細だった可能性もありますが、これは明らかに非論理的な考えです。通常のD式/宣言を使用してプラグイン文字列を解析する場合、なぜ彼らはこの特例を持つのでしょうか。
あるいは、静的foreachはifの後に複数のelseifを挿入できません。ここでは、D言語のメタプログラミングに対する根本的な誤解が見られます。静的foreachの本体では、一連の宣言文がインライン展開されます。D言語のメタプログラミングツールは、Cの可読性に壊滅的な影響を与えるため、Cのマクロのように不完全な宣言/文を記述することを禁止しています。ifとelseifの宣言を持つメタプログラミングのような不完全な宣言を記述したい場合は、文字列連携とプラグインを使用してください:
void main(){
int n;
import std.writeln;
mixin((string[] list){
string ret = "if(n == "~list[0]~"){\nwriteln(`"~list[0]~"`);\n";
foreach(item; list[1..$]){
ret ~= "}else if(n == "~item~"){\nwriteln(`"~item~"`);\n";
}
return ret ~ "}";
}(["1", "2", "3"]));
}
ldc2のコンパイル速度は非常に遅いです。コンパイル時間がこれほど遅く、これほど多くのメモリを使用すると聞くと、まず思いつくのは深刻なテンプレートの乱用です。これは以前LDC2で遭遇したコンパイル時間のボトルネックであり、特にリリースビルド時に顕著でした。
解決策は何でしょうか?まず、メタプログラミングを使用するたびに、コンパイラがそのコードを実行していることを認識する必要があります。したがって、そのコードの実行に長い時間がかかる場合、必然的にコンパイル時間が増加します。
コンパイラ設計ではこれを解決できません。私の場合、多くはネストされた静的foreachループ内でのテンプレート関数の再インスタンス化に起因します。代わりに、大きなプラグインを作成するCTFE関数を使用することをお勧めします。
しかし、ldc2は唯一の選択肢であることがあります。なぜなら、DMDにはバグがあるからです:これまでの数年間、プログラムのビルドを妨げるいくつかのエラーに遭遇しました。最新のものは、C++インターフェース時のコード生成エラーです。ここで言及されているエラーは以前に修正されました。重要なのは、これはマイクロソフト自身の呼び出し規約のドキュメントの誤りによって導入されたものです。
D言語はガベージコレクションを禁止するBetterCモードを提供します。しかし、このモードを使用すると、標準ライブラリをコンパイルできず、メタプログラミングが深刻に妨害されます。このセクションには多くの誤りがあります。BetterCは、Cランタイムへのフックを置き換えるdランタイムのラッパーであり、基本的なdランタイム関連関数(assertなど)が依然として有効であることを意味します。
GCはdランタイムの一部であるため、消えます。Phobos(Dの標準ライブラリ)の一部は使用できず、多くのメタプログラミングツール(std.traits/std.metaなど)は依然として使用可能です。
この違いに注意してください。なぜランタイムからガベージコレクタを削除すると、コンパイル時にGCの使用が停止するのでしょうか?コンパイラのインタープリタは、生成された実行時コードと同じランタイムを使用しないのでしょうか?いいえ、もちろんそうではありません。
デフォルトでは、D言語コンパイラはコンパイル時にさえGCを使用しませんが、メモリ消費が少ない場合は有効にできます。彼が遭遇した問題は、GC/DRuntimeのランタイム関数を使用すると、BetterCではコンパイルできない(またはリンカーエラーが発生する)ため、自然にCTFE文字列連携がBetterCでは不可能だと考えたことです。
これは完全に間違っています。実行時関数(実行時コードで使用するかどうかに関係なく)を記述するたびに、コンパイラはそれに対してコードを生成しようとします。関数内でCTFE専用のコードパスを作成できますが、この機能はifには適用されますが、静的foreachには適用されないため、ここでは役立ちません。
コンパイラが実行時コードの生成を試みない関数を作成する必要があります。ラムダが必要です:
// ラムダではない、実行時コードを生成:
string catRT(string a, string b) => a ~ b;
// エラー: "a"~"b"式の配列連結にはbetterCで利用できないGCが必要です。
// 列挙型にラムダを割り当て:
enum catCT = (string a, string b) => a ~ b; // OK
もちろん、実行時にこれを呼び出すとエラーが発生します。
間違ったデバッグ情報とガベージコレクションの実際の必要性は問題の破壊者です。Dユーザーとして、ガベージコレクションが問題の破壊者であると考えるのは奇妙ですが、リアルタイム速度が重要なアプリケーションでは理解できます。しかし、D言語はdランタイムがない場合でも完全に使用可能な言語です。なぜなら、簡単にラッパーを作成し、~をオーバーロードし、既存のC/C++ライブラリを使用してPhobosライブラリの代わりにカスタムテンプレートの「配列」構造体を使用できるからです。
実行ファイルサイズを気にしない場合は、手動メモリ割り当てとGCを混合使用するだけです。これにより、より多くのDライブラリを使用できます。良いアロケータを選択しない限り、または多くの大きな長期的なGCアロケータを手動アロケータに交換しない限り(GCヒースキャンを減らすため)、節約される時間はおそらく~0になります。
BitBasingの素晴らしい記事を参照してください。
2022年に名前付き引数渡しができない言語があるのはなぜですか?名前付き引数のDIPは2020年に受け入れられ、現在は利用可能です。それだけです!D言語の長いブログ記事が好きなら、いくつかを読むことをお勧めします。
中国語のユーザーはもちろんここにいます。