あなたがDプログラミング言語で行った、または行ったのを見た中で最もクールで実用的なメタプログラミングハックは何ですか?ある程度実用的なとは、たとえば、コンパイル時のレイトレーサーを除外することを意味します。
12 に答える
任意精度型コンパイル時に (コンパイラが行う前に) ASM コードを生成します。
ScrappleツールのDParseは、テンプレート化されたパーサージェネレーターです。ただし、ldcはコンパイル時GCが機能している唯一のDコンパイラです(ただし、それでも2、3の奇妙なランダムクラッシュが発生します)。私はそれを少し試してみましたが、構成ファイルの解析などの興味深いことを行うことができますが、コンパイル時のGCが完全に実行されるまで、大きなことはできません。
最もクールな例を挙げると、 Kirk McDonald のPyD (および他の同様のバインディング) と言わざるを得ません。これらは、複雑なコード生成だけでなく、多くの異なる型の検出と処理において膨大な量の作業を行っているからです。
とはいえ、 BLADEは技術的にはテンプレートではなく CTFE を使用するため、PyD が勝つだけです。
個人的には、D テンプレートは私の研究プロジェクトで広く使用されています。これは、モジュールが独自のプライベート データ型を定義できるシミュレーション フレームワークです。新しいユーザー タイプをフレームワークに公開するには、そのタイプの XML パーサーと関連するネットワーク シリアライゼーション/デシリアライゼーション コードを作成する 1 行のコードが必要です。
D/Objective-C Bridgeはテンプレートを使用して、D で Cocoa オブジェクトを操作できるようにします。
1 つの例は、ユーザー指定のレイアウトから始まるビット フィールド操作のコードを生成する D の標準ライブラリのビットフィールド機能です。
Tuple 機能は別の例です。ユーザーが指定した型とオプションの名前に基づいてタプルを生成します。名前付きフィールドを注入することを除いて、そこには多くの生成的な効果はありませんが、それは説明的な例だと思います。
Lambert のエクスプロイトを知らずに、memoize を標準ライブラリに追加しました。ドキュメントについてはこちら、コードについてはこちら、関連するニュースグループのディスカッションについてはこちらを参照してください。
私が取り組んだもう 1 つの機能は、積分関数または実数値関数を集計する高次関数です (たとえば、高速指数関数を提供します)。それはまだリリースの準備ができていません。
コンパイル中にオブジェクトの作成が許可されると、コンパイル中にすべてのオートマトン生成を行う正規表現エンジンなどを簡単に作成できます。
私のお気に入りは、tools.base の ElemType と KeyType です。
template ElemType(T) {
alias typeof((function() {
foreach (elem; Init!(T)) return elem; assert(false);
})()) ElemType;
}
template KeyType(T) {
alias typeof((function() {
foreach (key, elem; Init!(T)) return key; assert(false);
})()) KeyType;
}
統一された型のテンプレート構造体(ユニット エラーを許可しません。)
コンパイル時の文字列ハッシュ。これを使用して、コードに埋め込まれた文字列を難読化できます。「ハッシュ」で検索してみてください。そのページには他にもかなりの数の興味深いサンプルがあります。
私が質問したときにこれは存在しなかったので、私は自分の質問に答えます。テンプレートとコンパイル時のイントロスペクションを使用して、コンパイラ内で実行する代わりに、任意に複雑なユーザー定義型のポインタオフセット情報を生成して正確なヒープスキャンを可能にするガベージコレクターへのパッチを作成しました。
ヘッダーがこれである memoize() 関数を書きました (コードはここに貼り付けるには少し長いです):
auto memoize(TFunc)(TFunc func);
関数のアドレスを指定すると、元の関数の戻り値をキャッシュする厳密に型指定されたデリゲート (元の関数と同じ署名と戻り値の型) が返されるため、同じパラメーターで 2 回呼び出すことができます。基になる関数を 1 回だけ呼び出します。たとえば、指数関数的ではなく線形時間で実行されるフィボナッチ数列のメモ化された「再帰的」定義は次のとおりです。
uint fib(uint n) { return n > 0 ? n > 1 ? memoize(&fib)(n - 1) + memoize(&fib)(n - 2) : 1 : 0; }
fib(1000); のように、通常どおり呼び出すことができます。
編集:リンクを投稿した以前のコードはかなりひどいものでした。このバージョンはよりエレガントです。
Streamオブジェクトから単純な構造を読み書きするためのミックスイン:
template TStructReader() {
private alias typeof(*this) T;
static T opCall(Stream stream) {
assert(stream.readable);
T ret; stream.readExact(&ret, T.sizeof);
return ret;
}
}
template TStructWriter() {
private alias typeof(*this) T;
void write(Stream stream) {
assert(stream.writeable);
stream.writeExact(this, T.sizeof);
}
}
次のように使用します。
align (1) struct MyStruct {
... definitions here ...
mixin TStructReader;
mixin TStructWriter;
}
auto ms = MyStruct(stream);
ms.write(stream);
LuaDはまた、Lua とシームレスにやり取りするためにメタプログラミングを広く使用しています。1 つのコマンドでクラス全体を登録できます。