2010-09-30 45 views
2

比方說,我有一個類,像這樣:同步的任意屬性的對象透明地d

class Gerbil{ 
    int id; 
    float x,y,z; 
} 

讓我們進一步說,這是一個實時仿真,我有一臺服務器/客戶端的設置和我的一部分在服務器端更改屬性:

//... 
gerbil.x = 9.0; 
//... 

現在我想將此更改發送到客戶端以同步世界狀態。然而,問題是我有可能大量的沙鼠,而且這些沙鼠也可能具有很長的特性列表 - 不僅如此處所描繪的x,y,z那樣。

我的問題是:有沒有一種方法,我們可以透明地截取這些屬性分配,並從它們編譯差異?

從閱讀D參考我得到的印象opAssign可能是正確的事情,只有實際上沒有如何使用它的例子? (D Ref./opAssign)我想這將是這個樣子,但我只是從臀部射擊:

void opAssign(string name)(float val){ //Just guessing here 
    if(name in floatProps){ 
     if(isServer){ 
      changedProps.push(this.id, name, val); 
     } 
     floatProps[name] = val; 
    } 
} 

然後opAssign將被稱爲當我們這樣做:

gerbil.x = 9.0; //Same as gerbil.opAssign!("x")(9.0) ?? 

除了可能是錯誤的語法,這是朝着正確方向邁出的一步嗎?什麼是正確的語法?性能如何?它看起來可能很慢?有沒有更快,更「直接」的方式呢?

我真的很想避免這裏有詳細的設置,如:

gerbil.incProp(Prop.X, 9.0); 

感謝您的時間。

回答

3

重載opAssign()就像在C++中重載賦值運算符一樣。它用於分配給對象本身,而不是其成員之一。它真的不會做你想做的事。我相信你會得到最接近的是屬性:

class Gerbil 
{ 
public: 

    @property int id() 
    { 
     return _id; 
    } 

    @property id(int newID) 
    { 
     //... Do whatever interception you want. 
     _id = newID; 
    } 

    @property float x() 
    { 
     return _x; 
    } 

    @property x(float newX) 
    { 
     //... Do whatever interception you want. 
     _x = newX; 
    } 

    @property float y() 
    { 
     return _y; 
    } 

    @property y(float newY) 
    { 
     //... Do whatever interception you want. 
     _y = newY; 
    } 

    @property float z() 
    { 
     return _z; 
    } 

    @property z(float newZ) 
    { 
     //... Do whatever interception zou want. 
     _z = newZ; 
    } 

private: 

    int _id; 
    float _x, _y, _z; 
} 

@property使屬性的語法,這樣就可以使用該功能,就好像它是一個變量。所以,

//... 
auto copyOfGerbilX = gerbil.x; //translates to gerbil.x() 
gerbil.x = 9.0; //translates to gerbile.x(9.0) 
//... 

現在的法律,即使x是一個函數,而不是一個變量。您可以在函數中插入任何您想要的特殊處理代碼。由於用於訪問變量的語法就像是公共成員變量一樣,您可以自由地重構代碼,以便在您的類定義中使它們成爲屬性或公共成員變量(假設您還沒有嘗試過就像接受他們的地址一樣,因爲這並不意味着變量作爲一個函數是相同的)。

但是,如果你正在尋找的是一種不需要自己完成所有這些功能的通用方法,那麼它就沒有直接的構造。我相信你可以用編譯時反射和字符串mixins或模板mixins來完成它,它會查看你的變量列表,然後爲你生成每個屬性函數。但是,額外的處理代碼對於每個函數都必須基本相同,並且您必須小心所生成的代碼真的是您想要的。我確信這是可行的,但我不得不解決這個問題,以制定一個可行的解決方案。

要產生這樣的代碼,你需要看__traitsstd.traits的編譯時反射和template mixinsstringmixins代碼生成。我會三思而後行,而不是手工編寫代碼。它應該是非常可行的,但它不一定很容易,調試它可能是有趣的,如果你將不得不使用D模板和mixin才能使它正確。

但基本上,你要找的是使用@property函數,以便你可以添加你的處理程序代碼,然後可能使用編譯時反射與mixin一起爲你生成代碼,但生成這樣的代碼是一個相當先進的技術,所以你可能要等待嘗試,直到你更有經驗與D.

+0

我明白了。屬性和mixin聽起來很有趣。我會嘗試,並在需要時採用手動攔截代碼。謝謝! – 0scar 2010-09-30 23:22:34

+0

哇!現在我更愛D了。 :) – qeek 2010-10-06 19:38:48

4

建立在喬納森的回答,我在我的一些庫的使用這樣的代碼:

public template property(string name, T) { 
    mixin(`protected T _`~name~`;` ~ 
     propertyGetter!(name, T) ~ propertySetter!(name, T)); 
} 
public template property(string name, T, T def) 
{ 
    mixin(`protected T _`~name~` = `~def.stringof~`;` ~ 
     propertyGetter!(name, T) ~ propertySetter!(name, T)); 
} 
template propertyGetter(string name, T) { 
    enum propertyGetter = `public T `~name~`(){ return _`~name~`; }`; 
} 
template propertySetter(string name, T) { 
    enum propertySetter = `public typeof(this) `~name~`(T value){ _`~name~` = value;`~ 
       `/* notify somebody that I've changed here */`~ 
       `return this; }`; 
} 

mixin字符串有點難看,但它們保留了適當的行數。

我屬性添加到我的課是這樣的:

class Gerbil { 
    mixin property!("id", int); 
    mixin property!("x", float); 
    mixin property!("y", float, 11.0); // give this one a default value 
} 

如果你願意,你可以添加一些代碼到該通知的某種顯示器,它已經改變了propertySetter模板(通過ID,屬性名,和新的價值)。然後,監視器可以將此信息傳輸到服務器端的相應監視器,該服務器端將查找具有正確標識的對象並將指定的屬性設置爲新值。

+0

好的謝謝你的例子!我並不完全使用'template'和'enum'。你可以創建函數枚舉嗎?看起來很酷:) – 0scar 2010-09-30 23:29:42

+1

這就是所謂的同名模板(如果你會注意到,枚舉與模板名稱相同)。實例化時,模板被替換爲枚舉的值。這是在編譯時生成值的一種方法。在這種情況下,他正在構建字符串mixin中使用的字符串。 – 2010-10-01 00:50:50