www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Problem with Point property

reply Henning Hasemann <hhasemann web.de> writes:
Hi all, say I have a structure Point and an attribute position like this:

struct Point {
  int x, y;
}

class Foo {
  Point position;
}

So far, so good. Now I want to substitude the attribute with a property
(because I want to react on changes of the position immediately)
The setter is no Problem:

  void position(Point p) { mPosition = p; react(); }

The getter indeed is. If I wrote:

  Point position() { return mPosition; }

this would break expressions like

Foo().position.x = 5;

which I happen to use often.
The solutions I see are

* Return a proxy object instead of a real Point.
  Bad because the return type is not Point anymore.

* Return a Point*, would break types too I dont
  know if these two even work.

* Turn Point into a class so I return a reference.
  Possible, but Point really just carries 2 integers
  and I either had to change all "Point(...)"'s into "new Point(...)"'s,
  or implement opCall for a class which I'm not sure if it is the best
  option, since it may be misleading.

Are there other ways? Can I somehow return a reference to a struct
without "leaving" the type?

What would you do?

TIA,
Henning

-- 
v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR
hackerkey.com
Mar 06 2007
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Henning Hasemann wrote:
 Hi all, say I have a structure Point and an attribute position like this:
 
 struct Point {
   int x, y;
 }
 
 class Foo {
   Point position;
 }
 
 So far, so good. Now I want to substitude the attribute with a property
 (because I want to react on changes of the position immediately)
 The setter is no Problem:
 
   void position(Point p) { mPosition = p; react(); }
 
 The getter indeed is. If I wrote:
 
   Point position() { return mPosition; }
 
 this would break expressions like
 
 Foo().position.x = 5;
 
 which I happen to use often.
 The solutions I see are
 
 * Return a proxy object instead of a real Point.
   Bad because the return type is not Point anymore.
 
 * Return a Point*, would break types too I dont
   know if these two even work.
 
 * Turn Point into a class so I return a reference.
   Possible, but Point really just carries 2 integers
   and I either had to change all "Point(...)"'s into "new Point(...)"'s,
   or implement opCall for a class which I'm not sure if it is the best
   option, since it may be misleading.
 
 Are there other ways? Can I somehow return a reference to a struct
 without "leaving" the type?
 
 What would you do?
 
 TIA,
 Henning
 

If you return a pointer, you can still use the . operator, so I think foo().point.x; should work just fine (both syntactically and semantically) However the implication is that if you have a function that takes a point struct object, you either have to change that function to take a point pointer, or dereference the property every time you pass it to functions.
Mar 06 2007
prev sibling next sibling parent "Chris Miller" <chris dprogramming.com> writes:
On Tue, 06 Mar 2007 16:29:14 -0500, Henning Hasemann <hhasemann web.de> =
 =

wrote:

 Hi all, say I have a structure Point and an attribute position like th=

 struct Point {
   int x, y;
 }

 class Foo {
   Point position;
 }

 So far, so good. Now I want to substitude the attribute with a propert=

 (because I want to react on changes of the position immediately)
 The setter is no Problem:

   void position(Point p) { mPosition =3D p; react(); }

 The getter indeed is. If I wrote:

   Point position() { return mPosition; }

 this would break expressions like

 Foo().position.x =3D 5;

This should be easy enough for the compiler to detect and automatically = = call the matching setter (if none, error). But the compiler doesn't even= = detect some basic operators on properties, so...
Mar 06 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
 If you return a pointer, you can still use the . operator, so I think
 foo().point.x;
 should work just fine (both syntactically and semantically)
 
 However the implication is that if you have a function that takes a 
 point struct object, you either have to change that function to take a 
 point pointer, or dereference the property every time you pass it to 
 functions.

Yeah, I tried this before, but got exactly the described Problem and didnt wand to change my functions to receive Point* because that would seem strange and inconsistent. I'd have to use Point* everywhere maybe but then I could as well make Point a class I think. Henning -- v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR hackerkey.com
Mar 06 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
 Foo().position.x = 5;

This should be easy enough for the compiler to detect and automatically call the matching setter (if none, error). But the compiler doesn't even detect some basic operators on properties, so...

A first test lets me think, the compiler reads this as: Point tmp = (new Foo()).position; tmp.x = 5; speak: The position is copied from the object and the assignment is made on the copy. Henning -- v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR hackerkey.com
Mar 06 2007
prev sibling next sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
Henning Hasemann wrote

 this would break expressions like
 Foo().position.x = 5;
 which I happen to use often.

The problem seems to stem from the often used breaking of the data encapsulation in the class Foo. I already wrote about my _feeling_ that using properties as substitutes for field accesses is inherently wrong because it also breaks data encapsulation. Getters might be better seen as delegates, capable of answering questions about the state of the instance they stem from at that point in time when they were requested. -manfred
Mar 06 2007
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
 The problem =B4seems to stem from the often used breaking of the data=20
 encapsulation in the class Foo.
=20
 I already wrote about my _feeling_ that using properties as substitutes=20
 for field accesses is inherently wrong because it also breaks data=20
 encapsulation.
=20
 Getters might be better seen as delegates, capable of answering=20
 questions about the state of the instance they stem from at that point=20
 in time when they were requested.
=20
 -manfred

So you're saying one should better write a setPosition method instead of a setter? Or how would you make it? The semantic is, that Foo really has a position, but when it is changed, it should inform another object because that holds Foo's sorted by y-positi= on. But that in turn is an implementation detail, a user of Foo should not notice nor care about: Maybe later on I'll just resort the Foo's from time to time or when I need them sorted or whatever. Henning --=20 v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR= hackerkey.com
Mar 07 2007
prev sibling parent reply Henning Hasemann <hhasemann web.de> writes:
I found a 'solution' myself which is aahh.. 'tricky'.
I extend the struct to be able to call the setter method
whenever its value changes.
To stop behaving it like a pointer, I misuse opCast as
a 'copy constructor'. So

Point p = (new Foo).position;
p.x = 5;

wont change the Foo's position, as it where when position was
a real member. 

Look here:

struct Point {
  private {
    int mX, mY;
    void delegate(Point) setter;
  }
  
  Point opCast() {
    Point p;
    p.setter = null;
    p.mX = x; p.mY = y;
    return p;
  }

  int x() {
    return mX;
  }
  void x(int x1) {
    mX = x1;
    if(setter !is null)
      setter(*this);
  }
  // skipped the same for y
}

To be used like:

class Bar {
  private Point mPosition;
  this() {
    mPosition.setter = &position;
  }
  Point position() {
    return mPosition;
  }
  void position(Point p) {
    mPosition = p;
  }
}

  Bar b;

  // This actually changes the object
  b.position.x = 7;
  writefln(b.position.x); // 7

  // This as before, does not
  Point p = b.position; // here opCast is being called
  p.x = 5;
  writefln(b.position.x); // still 7


The only thing is Im not quite sure if using opCast this way is
portable across versions and compiler manufracturers,
anyone knowing something about that?

Henning




  


-- 
v4sw7Yhw4ln0pr7Ock2/3ma7uLw5Xm0l6/7DGKi2e6t6ELNSTVXb7AHIMOen5a2Xs5Mr2g5ACPR
hackerkey.com
Mar 07 2007
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Henning Hasemann wrote:
 I found a 'solution' myself which is aahh.. 'tricky'.
 I extend the struct to be able to call the setter method
 whenever its value changes.
 To stop behaving it like a pointer, I misuse opCast as
 a 'copy constructor'. So
 
 Point p = (new Foo).position;
 p.x = 5;
 
 wont change the Foo's position, as it where when position was
 a real member. 
 
 Look here:
 
 struct Point {
   private {
     int mX, mY;
     void delegate(Point) setter;
   }
   
   Point opCast() {
     Point p;
     p.setter = null;
     p.mX = x; p.mY = y;
     return p;
   }
 
   int x() {
     return mX;
   }
   void x(int x1) {
     mX = x1;
     if(setter !is null)
       setter(*this);
   }
   // skipped the same for y
 }
 
 To be used like:
 
 class Bar {
   private Point mPosition;
   this() {
     mPosition.setter = &position;
   }
   Point position() {
     return mPosition;
   }
   void position(Point p) {
     mPosition = p;
   }
 }
 
   Bar b;
 
   // This actually changes the object
   b.position.x = 7;
   writefln(b.position.x); // 7
 
   // This as before, does not
   Point p = b.position; // here opCast is being called
   p.x = 5;
   writefln(b.position.x); // still 7
 
 
 The only thing is Im not quite sure if using opCast this way is
 portable across versions and compiler manufracturers,
 anyone knowing something about that?
 
 Henning
 

Actually, that's rather slick, and worth studying, even if it turns out not to be optimal. (Which with a significant number of such properties, it couldn't possibly be.) I really can't offer anything better off hand. -- Chris Nicholson-Sauls
Mar 07 2007