代码之家  ›  专栏  ›  技术社区  ›  0scar

在D中透明地同步对象中的任意属性

  •  2
  • 0scar  · 技术社区  · 16 年前

    假设我有这样一节课:

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

    我们进一步说,这是实时模拟的一部分,我在其中设置了服务器/客户机,并更改了服务器端的属性:

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

    现在我想把这个更改发送给客户机来同步世界状态。然而,问题是我可能有大量的沙鼠,这些沙鼠也可能有很长的财产清单,不仅仅是x,y,z,如这里所描述的。

    有没有一种方法可以透明地截取这些属性赋值,并从中编译一个diff?

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

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

    除了可能的错误语法之外,这是朝着正确的方向迈出的一步吗?正确的语法是什么?表现如何?看起来很慢吧?有没有更快捷、更“直接”的方法?

    gerbil.incProp(Prop.X, 9.0);
    

    谢谢你的时间。

    2 回复  |  直到 16 年前
        1
  •  3
  •   Jonathan M Davis    16 年前

    重载opAccess()类似于在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 是函数而不是变量。您可以在函数中插入任何需要的特殊处理代码。而且,因为用于访问变量的语法就像它们是公共成员变量一样,所以您可以自由地重构代码,在类定义中使它们成为属性或公共成员变量之间进行切换(假设您没有尝试过像获取它们的地址这样的操作,因为这对于变量作为函数)。

    然而,如果你所寻找的是一种通用的方法,不必自己完成所有这些功能,那么没有直接的构造。我相信您可以使用编译时反射和字符串混合或模板混合来完成,它们将查看变量列表,然后为您生成每个属性函数。但是,对于每个函数,额外的处理代码必须基本相同,并且必须注意生成的代码是您真正想要的。我确信这是可行的,但我得在这个问题上努力一段时间才能找到一个可行的解决办法。

    要生成这样的代码,您需要 __traits 和 std.traits template mixins 和 string mixins 用于代码生成。我会三思而后行生成这样的代码,而不是手写。它应该是相当可行的,但它不一定很容易,调试它可能是有趣的,如果你必须相当好地与D模板和mixin得到正确的。

    但从本质上讲,您需要的是使用@property函数,这样您就可以添加处理程序代码,然后可能使用编译时反射和mixin来为您生成代码,但是生成这样的代码是一种相当高级的技术,因此您可能希望等到您对D有了更多的经验之后再尝试。

        2
  •  4
  •   Justin W    16 年前

    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
    }