views:

70

answers:

2

Let's say I have a class like so:

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

Let's further say this is part of a real-time simulation where I have a server/client setup and I change a property on the server-side:

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

Now I want to send over this change to the client to synchronize the world state. However, the problem is I have potentially vast amounts of gerbils, and these gerbils also potentially have long lists of properties—not just x,y,z as depicted here.

My question is: Is there a way we can intercept these property assignments, transparently, and compile a diff from them?

From reading the D reference I got the impression opAssign might be the right thing, only there's actually no examples of how to use it? (D Ref. / opAssign) I suppose it would look something like this, but I'm just shooting from the hip:

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

And then opAssign would be called when we do:

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

Apart from possibly wrong syntax, is this a step in the right direction? What is the right syntax? What about performance? It looks like it could be quite slow? Is there a faster, more "direct" way of this?

What I'd really like to avoid here are elaborate setups like:

gerbil.incProp(Prop.X, 9.0);

Thanks for your time.

+3  A: 

Overloading opAssign() is like overloading the assignment operator in C++. It's for assigning to the object itself, not one of its members. It's really not going to do what you want. I believe that the closest that you're going to get is properties:

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 enables property syntax so that you can use the function as if it were a variable. So,

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

is now legal even though x is a function rather than a variable. You can insert whatever special handling code you want in the functions. And because the syntax used to access the variables is just as if they were public member variables, you can freely refactor your code to switch between having them be properties or public member variables in your class definition (assuming that you haven't tried to do something like take their address, since that doesn't mean the same thing for a variable as a function).

However, if what you're looking for is a generic way to not have to do all of those functions yourself, there is no direct construct for it. I believe that you could do it with compile-time reflection and string mixins or template mixins which would look at the list of your variables and then generate each of the property functions for you. However, then the extra handling code would have to be essentially the same for each function, and you'd have to be careful that the generated code was really what you wanted. I'm sure that it's feasible, but I'd have to work on the problem for a bit to produce a workable solution.

To generate such code, you'd need to look at __traits and std.traits for the compile-time reflection and at template mixins and string mixins for the code generation. I'd think twice about generating the code like that though rather than writing it by hand. It should be quite doable, but it won't necessarily be easy, debugging it could be entertaining, and if you're going to have to be fairly good with D templates and mixins to get it right.

But essentially, what you're looking for is to use @property functions so that you can add your handler code and then possibly use compile-time reflection along with mixins to generate the code for you, but generating code like that is a fairly advanced technique, so you may want to wait to try that until you're more experienced with D.

Jonathan M Davis
I see. Properties and mixins sounds interesting. I'll try that and resort to manual interception-code when needed. Thanks!
0scar
WOW! Now I love D even more. :)
qeek
+4  A: 

Building on Jonathan's answer, I use code like this in a number of my libraries:

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

The mixin strings are a bit ugly, but they preserve the proper line count.

I add properties to my classes like this:

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

If you wanted, you could add some code to the propertySetter template that notified some sort of monitor that it had changed (passing id, property name, and new value). Then the monitor could transmit this info to a corresponding monitor on the server side who would find the object with proper id and set the specified property to the new value.

Justin W
Alright thanks for the example! I don't quite get the use of `template` and `enum` together. Can you create function enums? Seems cool anyway :)
0scar
It's what's called an eponymous template (if you'll notice, the enum has the same name as the template). When instantiated, the template is replaced with the value of the enum. It's a way to generate values at compile time. In this case, he's building strings to use in string mixins.
Jonathan M Davis