C++のテンプレートメカニズムは、誤ってテンプレートメタプログラミングに役立ちました。一方、Dは特にこれを容易にするために設計されました。そして、どうやらそれは理解するのがさらに簡単です(または私は聞いたことがあるので)。
私はDの経験はありませんが、好奇心旺盛です。テンプレートのメタプログラミングに関して、Dでできること、C++ではできないことは何ですか。
Dでのテンプレートメタプログラミングを支援する2つの最大の要素は、テンプレート制約とstatic if
です。どちらもC++は理論的に追加でき、これは非常に有益です。
テンプレート制約を使用すると、テンプレートをインスタンス化できるようにするためにtrueでなければならない条件をテンプレートに設定できます。たとえば、これはstd.algorithm.find
のオーバーロードの1つのシグネチャです。
R find(alias pred = "a == b", R, E)(R haystack, E needle)
if (isInputRange!R &&
is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
このテンプレート関数をインスタンス化できるようにするには、型R
がstd.range.isInputRange
で定義されている入力範囲である必要があります(したがって、isInputRange!R
はtrue
でなければなりません) 、および指定された述語は、指定された引数でコンパイルされ、暗黙的にbool
に変換可能な型を返すバイナリ関数である必要があります。テンプレート制約の条件の結果がfalse
の場合、テンプレートはコンパイルされません。これにより、テンプレートが指定された引数でコンパイルされない場合にC++で発生する厄介なテンプレートエラーから保護されるだけでなく、テンプレートの制約に基づいてテンプレートをオーバーロードできるようになります。たとえば、find
の別のオーバーロードがあります。
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
if (isForwardRange!R1 && isForwardRange!R2
&& is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)
&& !isRandomAccessRange!R1)
引数はまったく同じですが、制約が異なります。したがって、異なる型は同じテンプレート関数の異なるオーバーロードで機能し、find
の最適な実装を各型に使用できます。 C++でそのようなことをきれいに行う方法はありません。通常のテンプレート制約で使用される関数とテンプレートに少し精通しているので、Dのテンプレート制約はかなり読みやすいですが、C++で非常に複雑なテンプレートメタプログラミングが必要な場合でも、このようなものを試してみると、平均的なプログラマではできません理解できるようになる、ましてや実際に自分で行う。ブーストは、この典型的な例です。それはいくつかの素晴らしいことをしますが、それは信じられないほど複雑です。
static if
は状況をさらに改善します。テンプレート制約の場合と同様に、コンパイル時に評価できるすべての条件を使用できます。例えば.
static if(isIntegral!T)
{
//...
}
else static if(isFloatingPoint!T)
{
//...
}
else static if(isSomeString!T)
{
//...
}
else static if(isDynamicArray!T)
{
//...
}
else
{
//...
}
コンパイルされるブランチは、最初にtrue
と評価される条件によって異なります。そのため、テンプレート内で、テンプレートのインスタンス化に使用されたタイプに基づいて、またはコンパイル時に評価できる他の何かに基づいて、その実装の一部を特殊化できます。たとえば、core.time
は
static if(is(typeof(clock_gettime)))
システムがclock_gettime
を提供するかどうかに基づいて異なる方法でコードをコンパイルします(clock_gettime
がある場合はそれを使用し、それ以外の場合はgettimeofday
を使用します)。
恐らく、Dがテンプレートを改善した最も目立った例は、私のチームがC++で遭遇した問題です。与えられた型が特定の基本クラスから派生したものかどうかに基づいて、テンプレートを異なる方法でインスタンス化する必要がありました。 このスタックオーバーフローの質問 に基づくソリューションを使用することになりました。機能しますが、あるタイプが別のタイプから派生しているかどうかをテストするだけではかなり複雑です。
ただし、Dでは、:
演算子を使用するだけで済みます。例えば.
auto func(T : U)(T val) {...}
T
が暗黙的にU
に変換できる場合(T
がU
から派生した場合と同様)、func
はコンパイルされます。一方、T
が暗黙的にU
に変換できない場合、変換できません。 That単純な改善により、基本的なテンプレートの特殊化でさえも(テンプレートの制約やstatic if
がなくても)より強力になります。
個人的には、コンテナや<algorithm>
の関数を除いて、C++でテンプレートを使用することはめったにありません。テンプレートを使用するのは非常に面倒だからです。それらは醜いエラーを引き起こし、空想的なことを行うことは非常に困難です。少しでも複雑なことを行うには、テンプレートとテンプレートのメタプログラミングに非常に熟練している必要があります。ただし、Dのテンプレートを使用すると、非常に簡単なので、いつでも使用できます。エラーは理解し、処理するのがはるかに簡単です(ただし、エラーは通常、テンプレート化されていない関数で発生するエラーよりもさらに悪いです)。そして、派手なメタプログラミングで言語を強制的に実行させる方法を理解する必要はありません。 。
C++がDが持っているこれらの能力の多くを獲得できなかった理由はありません(C++の概念は、それらが整理される場合に役立ちます)が、テンプレート制約とstatic if
に類似した構成を持つ基本的な条件付きコンパイルを追加するまでC++、C++テンプレートは、使いやすさとパワーの点でDテンプレートと比較できません。
Dテンプレートシステムの信じられないほどのパワー(TM)を示すのに適したものは何もないと思います このレンダラー 私は何年も前に見つけました:
はい!これは実際にはコンパイラによって生成されるものです...これは実際に「プログラム」であり、非常にカラフルなプログラムです。
ソースがオンラインに戻ったようです。
Dメタプログラミングの最良の例は、C++ BoostおよびSTLモジュールと比較して、Dメタプログラミングを多用するD標準ライブラリモジュールです。 Dの std.range 、 std.algorithm 、 std.functional および std.parallelism を確認してください。これらのどれもC++で実装するのは簡単ではありません。少なくとも、Dモジュールが備えているようなクリーンで表現力豊かなAPIを使用する場合はそうです。
DメタプログラミングであるIMHOを学ぶための最良の方法は、次のような例です。 Andrei Alexandrescu(Dに深く関わったC++テンプレートのメタプログラミングの第一人者)が作成したstd.algorithmとstd.rangeへのコードを読むことで、主に学びました。次に、学んだことを使用してstd.parallelismモジュールに貢献しました。
また、DにはC++ 1xのconstexpr
と同様のコンパイル時関数評価(CTFE)がありますが、実行時に評価できる関数の大規模で成長するサブセットを変更せずに評価できるという点でより一般的です。コンパイル時間。これはコンパイル時のコード生成に役立ち、生成されたコードは string mixins を使用してコンパイルできます。
Dでは、静的な テンプレートパラメータの制約 を簡単に適用して、実際のテンプレート引数に応じて 静的if でコードを記述できます。
テンプレートの特殊化やその他のトリック(boostを参照)を使用して、C++の単純なケースでそれをシミュレートすることは可能ですが、PITAであり、非常に制限されているため、コンパイラーは型に関する多くの詳細を公開しません。
C++で本当にできないことの1つは、高度なコンパイル時コード生成です。
これは、カスタムメイドのmap()
を実行するDコードで、参照によって結果を返します。
長さ4の2つの配列を作成し、maps要素の対応する各ペアを最小値の要素に割り当て、それに50を乗算し、結果を元の配列に保存します。
注意すべきいくつかの重要な機能は次のとおりです。
テンプレートは可変です:map()
は任意の数の引数を取ることができます。
コード(比較的)短い!コアロジックであるMapper
構造体はわずか15行ですが、それでもわずかな量で多くのことを実行できます。私のポイントは、これはC++では不可能であるということではありませんが、それは確かにコンパクトでクリーンではありません。
import std.metastrings, std.typetuple, std.range, std.stdio;
void main() {
auto arr1 = [1, 10, 5, 6], arr2 = [3, 9, 80, 4];
foreach (ref m; map!min(arr1, arr2)[1 .. 3])
m *= 50;
writeln(arr1, arr2); // Voila! You get: [1, 10, 250, 6][3, 450, 80, 4]
}
auto ref min(T...)(ref T values) {
auto p = &values[0];
foreach (i, v; values)
if (v < *p)
p = &values[i];
return *p;
}
Mapper!(F, T) map(alias F, T...)(T args) { return Mapper!(F, T)(args); }
struct Mapper(alias F, T...) {
T src; // It's a Tuple!
@property bool empty() { return src[0].empty; }
@property auto ref front() {
immutable sources = FormatIota!(q{src[%s].front}, T.length);
return mixin(Format!(q{F(%s)}, sources));
}
void popFront() { foreach (i, x; src) { src[i].popFront(); } }
auto opSlice(size_t a, size_t b) {
immutable sliced = FormatIota!(q{src[%s][a .. b]}, T.length);
return mixin(Format!(q{map!F(%s)}, sliced));
}
}
// All this does is go through the numbers [0, len),
// and return string 'f' formatted with each integer, all joined with commas
template FormatIota(string f, int len, int i = 0) {
static if (i + 1 < len)
enum FormatIota = Format!(f, i) ~ ", " ~ FormatIota!(f, len, i + 1);
else
enum FormatIota = Format!(f, i);
}
Dのテンプレート、文字列ミックスイン、およびテンプレートミックスインでの経験を書き留めました。 http://david.rothlis.net/d/templates/
Dで可能なことのフレーバーを提供する必要があります-C++では、識別子を文字列としてアクセスし、コンパイル時にその文字列を変換し、操作された文字列からコードを生成できるとは思いません。
私の結論:非常に柔軟性があり、非常に強力で、単なる常連が使用できますが、より高度なコンパイル時のメタプログラミングに関しては、リファレンスコンパイラは依然としてややバグがあります。
文字列操作、さらには文字列解析。
これはMPライブラリ であり、(多かれ少なかれ)BNFを使用して文字列で定義された文法に基づいて、適切なパーサーを再帰的に生成します。何年も触れていませんが、働いていた。
dでは、型のサイズとその型で使用可能なメソッドを確認し、使用する実装を決定できます
これは、たとえば core.atomic
モジュール
bool cas(T,V1,V2)( shared(T)* here, const V1 ifThis, const V2 writeThis ){
static if(T.sizeof == byte.sizeof){
//do 1 byte CaS
}else static if(T.sizeof == short.sizeof){
//do 2 byte CaS
}else static if( T.sizeof == int.sizeof ){
//do 4 byte CaS
}else static if( T.sizeof == long.sizeof ){
//do 8 byte CaS
}else static assert(false);
}
Dレイトレーシングの投稿に対抗するために、ここにC++コンパイル時レイトレーサー( metatrace )を示します。
(ちなみに、それは主にC++ 2003メタプログラミングを使用します。新しいconstexpr
sでより読みやすくなります)
Dのテンプレートメタプログラミングでは、C++ではできないような静かなことがいくつかあります。最も重要なことは、SO苦痛なしでテンプレートメタプログラミングを実行できることです。