2012-06-09 18 views
7

是否可以編寫單個模板函數來遞增不同結構的(數字)字段?例如:D中的結構和元組模板參數

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

struct Point 
{ 
    double x, y; 
} 

我想是這樣的:

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 
} 

我也試着寫接受元組函數模板,但元組總是被擴展,從而可以防止編譯器匹配正確的模板。 謝謝。

回答

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; 
} 

主要問題就在這裏,當然是,在這兩個模板約束太弱。所有你需要做的是在你的結構中有不可增加的類型,並且它不起作用。要解決這個問題最簡單的方法很可能會創建一個同名的模板,以測試它的你:

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;})); 
} 

如果你希望能夠增加所有都是遞增的字段,並獨自離開了別人,你倒是可能做這樣的事情:

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直接在使用ref,這樣的元素進行而更新不是讓他們的副本是更新。

+1

神奇的魔法! – YGL