Синхронизация произвольных свойств в объекте прозрачно в D - PullRequest
2 голосов
/ 30 сентября 2010

Допустим, у меня есть такой класс:

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

Допустим, это часть симуляции в реальном времени, где у меня настроен сервер / клиент, и я меняю свойство на стороне сервера:

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

Теперь я хочу отправить это изменение клиенту для синхронизации состояния мира. Однако проблема в том, что у меня есть потенциально огромное количество песчанок, и эти песчанки также потенциально имеют длинные списки свойств - не только x, y, z, как показано здесь.

У меня такой вопрос: Есть ли способ, которым мы можем прозрачно перехватить эти присвоения свойств и скомпилировать из них diff?

После прочтения ссылки на 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);

Спасибо за ваше время.

Ответы [ 2 ]

4 голосов
/ 01 октября 2010

Опираясь на ответ Джонатана, я использую такой код во многих моих библиотеках:

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, который уведомит какой-нибудь монитор о том, что он изменился (передавая идентификатор, имя свойства и новое значение). Затем монитор может передать эту информацию соответствующему монитору на стороне сервера, который найдет объект с правильным идентификатором и установит для указанного свойства новое значение.

3 голосов
/ 30 сентября 2010

Перегрузка 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 является функцией, а не переменной. Вы можете вставить любой специальный код обработки, который вы хотите в функции. А поскольку синтаксис, используемый для доступа к переменным, такой же, как если бы они были открытыми переменными-членами, вы можете свободно реорганизовать свой код, чтобы переключаться между тем, чтобы они были свойствами или открытыми переменными-членами в определении класса (при условии, что вы не пытались это сделать). что-то вроде взятия их адреса, поскольку для переменной это не означает то же самое, что и функция).

Однако, если вы ищете общий способ не выполнять все эти функции самостоятельно, для этого нет прямой конструкции. Я полагаю, что вы могли бы сделать это с помощью отражения во время компиляции и строковых или шаблонных смесей, которые будут смотреть на список ваших переменных и затем генерировать каждую из функций свойств для вас. Однако тогда дополнительный код обработки должен быть практически одинаковым для каждой функции, и вы должны быть осторожны, чтобы сгенерированный код был действительно тем, что вы хотели. Я уверен, что это выполнимо, но мне пришлось бы немного поработать над этой проблемой, чтобы найти работоспособное решение.

Чтобы сгенерировать такой код, вам нужно взглянуть на __ traits и std.traits для отражения во время компиляции и на шаблонных миксинов и string mixins для генерации кода. Я бы дважды подумал о генерации такого кода, а не писать его вручную. Это должно быть вполне выполнимо, но это не обязательно будет легко, отладка может быть интересной, и если вы собираетесь быть достаточно хорошими с D-шаблонами и миксинами, чтобы сделать это правильно.

Но, по сути, вам нужно использовать функции @property, чтобы вы могли добавить свой код обработчика, а затем, возможно, использовать отражение во время компиляции вместе с миксинами для генерации кода для вас, но генерация такого кода довольно продвинутая техника, так что вы можете подождать, чтобы попробовать ее, пока не разберетесь с D.

...