2012-06-09 13 views
7

異なる構造体の(数値)フィールドをインクリメントする単一のテンプレート関数を記述できますか?例えば:私もタプルを受け入れる関数テンプレートを書いて試してみましたが、タプルは常に展開され、正しいテンプレートに一致するから、コンパイラを防止する構造体とタプルのテンプレートパラメータD

T update(T, A)(T t, A a) 
if (is(T == struct)) 
{ 
    auto vals = t.tupleof; 
    foreach (i; 0 .. vals.length) { 
     vals[i] += a; // error: i cannot be read at compile time 
    } 
    return T(vals); // convert back to struct 
} 

struct Color 
{ 
    ubyte a,r,g,b; 
} 

struct Point 
{ 
    double x, y; 
} 

私はこのような何かを試してみました。おかげさまで

答えて

12

あなたがやっていることはやや奇妙なことですが、確かに可能です。最もナイーブ、インプレースの方法は、おそらく次のようになります。

void update(T)(ref T t) 
    if(is(T == struct)) 
{ 
    foreach(ref var; t.tupleof) 
     ++var; 
} 

おそらくそれをコピーし、更新された値を持つ新しいものを構築しようとするよりも、それをむしろ更新するだろう(コピーでそれを行うための最も簡単な方法私はあなたが本当にしたい場合には)あまりにも行うことができることを確信しているものの:

T update(T)(T t) 
    if(is(T == struct)) 
{ 
    auto copy = t; 

    foreach(ref var; copy.tupleof) 
     ++var; 

    return copy; 
} 

ここでの主な問題は、当然のことながら、これらの両方のテンプレート制約があまりにも弱いということです。あなたがしなければならないのは、あなたの構造体にunincrementable型があり、それは動作しません。それはおそらくあなたのためにそれをテストするための名を冠したテンプレートを作成することです解決するための最も簡単な方法:

T update(T)(T t) 
    if(isIncrementableStruct!T) 
{ 
    auto copy = t; 

    foreach(ref var; copy.tupleof) 
     ++var; 

    return copy; 
} 

template isIncrementableStruct(T) 
{ 
    enum isIncrementableStruct = is(T == struct) && 
           is(typeof({T t; foreach(var; t.tupleof) ++var;})); 
} 

をそして、あなたがincrementableあるすべてのフィールドをインクリメントし、一人で他の人を残すことができるようにしたい場合は、あなたおそらくような何かD」:要素があること、それらのコピーを持つのではなく、更新されたようにrefを使用して直接ながら、いずれの場合においても

T update(T)(T t) 
    if(is(T == struct)) 
{ 
    auto copy = t; 

    foreach(ref var; copy.tupleof) 
    { 
     static if(canIncrement!(typeof(var))) 
      ++var; 
    } 

    return copy; 
} 

template canIncrement(T) 
{ 
    enum canIncrement = is(typeof({T var; ++var;})); 
} 

を、あなたが表示される主なものを見逃しているとはtupleof上で反復処理を試みることでした更新しました。

+1

すばらしいウィザードリです! – YGL