www.digitalmars.com         C & C++   DMDScript  

D - Ack - Getters and setters inside themselves

reply Russ Lewis <russ deming-os.org> writes:
Writing my previous post, I ended up with a problem:

class Foo
{
   int bar;
public:
   int bar() { return bar; };
};

In the basic definition of getters and setters, the line
   return bar;
should call the getter for bar, causing an infinite loop!

We need some sort of specification in the language to avoid this.  You
could define that a setter or getter can't recursively call itself, but
we're still left with a pathological case:

class Confusion
{
   int a,b;
public:
   int a() { return b; };
   int b() { return a; };
};

I'm leaning toward the idea that in a class member function, it will
*always* use the underlying variable and not the getter and setter...but
even that's not totally clean.

class Money // another pathological case
{
   float money;
public:
   void money(float dollars) { money = 100*dollars; };
   float money() { return money/100; };

public:
   void Add(float dollars) { money = money+dollars; }; // probably NOT
what the programmer wanted
};

I haven't seen anything about friend classes (is a class a friend of
itself, like in Java?) - what happens when a friend class uses a
property?
Aug 21 2001
next sibling parent "LuigiG" <ask.me if.you.need.it> writes:
"Russ Lewis" <russ deming-os.org> wrote in message
news:3B82D980.B5E2E3E3 deming-os.org...
 I'm leaning toward the idea that in a class member function, it will
 *always* use the underlying variable and not the getter and setter...but

Seems the most logical to me.
Aug 22 2001
prev sibling parent reply "Anthony Steele" <asteele nospam.iafrica.com> writes:
"Russ Lewis" <russ deming-os.org> wrote in message
news:3B82D980.B5E2E3E3 deming-os.org...
 Writing my previous post, I ended up with a problem:

 class Foo
 {
    int bar;
 public:
    int bar() { return bar; };
 };

 In the basic definition of getters and setters, the line
    return bar;
 should call the getter for bar, causing an infinite loop!

 We need some sort of specification in the language to avoid this.  You
 could define that a setter or getter can't recursively call itself, but
 we're still left with a pathological case:

.. My experience from using properties in Delphi & liking the way that Delphi does them: This isn't really a problem in Delphi, and no special rules are needed, just a sensible syntax for property declarations, where the gettor, settor and instance var need to have different names in order to compile, just like everything else in the class. For e.g: class Foo { int m_bar; public: int bar() { return m_bar; }; }; Or something similar. You can't normally have a class with an instance var with the same name as a method, why should properties change that? See my previous comments for more detail. Yes, in Delphi it is possible to miscode so that the property gettor calls itself, the equivalent of forgetting the 'm_' prefix in the above class. Yes, the program hangs the first time you run it. It's easy to spot & easy to fix, so I don't see it as a real problem. I don't think that it is worth cluttering the language with a special feature for this.
Sep 04 2001
next sibling parent reply Axel Kittenberger <axel dtone.org> writes:
 Or something similar. You can't normally have a class with an instance var
 with the same name as a method, why should properties change that? See my
 previous comments for more detail.

Well thats not a must be. For Pascel it's true since you don't have to use brackets for (void) functions the compiler couldn't tell which one you mean. For C it's also true, since the function name without brackets returns a pointer to the function, but suprisingly in java (and taken over to dtone :o) you can have a field and a function name with the same name. The compile can eitherway tell from you syntax which one you mean. class cork { int a; void a() {return 5}; } cork.a = cork.a(); I guess it's pretty clear which 'a' is meant when. - Axel
Sep 04 2001
next sibling parent "e.sammer" <eric lifeless.net> writes:
Objective-C is the same. it will allow instance vars and methods to have 
the same name. (from Axel's example...)

 interface cork {
     int a;
}

- (void)a;

 end


--
e.sammer <eric lifeless.net>


Axel Kittenberger wrote:

Or something similar. You can't normally have a class with an instance var
with the same name as a method, why should properties change that? See my
previous comments for more detail.

Well thats not a must be. For Pascel it's true since you don't have to use brackets for (void) functions the compiler couldn't tell which one you mean. For C it's also true, since the function name without brackets returns a pointer to the function, but suprisingly in java (and taken over to dtone :o) you can have a field and a function name with the same name. The compile can eitherway tell from you syntax which one you mean. class cork { int a; void a() {return 5}; } cork.a = cork.a(); I guess it's pretty clear which 'a' is meant when. - Axel

Sep 05 2001
prev sibling parent reply "Anthony Steele" <asteele nospam.iafrica.com> writes:
 to dtone :o) you can have a field and a function name with the same name.
 The compile can eitherway tell from you syntax which one you mean.

Hm, is this name-overloading either necessary or desirable?
Sep 05 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 Hm, is this name-overloading either necessary or desirable?

Yes, at least if you don't have properties, in C++ you result having often the private field preceded with _ and a public access routine. class Node { class Noe * _parent; public Node * parent() {return _parent;} } Now the fatal typo that can happen quickly when coding Node itself, is you write just parent meaning _parent, and handle in reality with function-pointer to parent() - Axel
Sep 05 2001
parent reply "Anthony Steele" <asteele nospam.iafrica.com> writes:
 Hm, is this name-overloading either necessary or desirable?


news:<9n5u9u$1v5g$2 digitaldaemon.com>...
 Yes, at least if you don't have properties, in C++ you result having often
 the private field preceded with _ and a public access routine.

 class Node {
     class Noe * _parent;

     public Node * parent() {return _parent;}

But the key feature of your example is that you *don't* have a var and a function with the same name on the class. I repeat - I cannot think of any situation where it would be desirable or even necessary to allow a var and a function with the same name on the same class. Perhaps this is just brain-damage from too much Object-Pascal, but if I saw a class like class Foo { int bar; public int bar() ... } My first impulse would not be to praise the coder's use of adavanced C-language-family features, but to slap them until they thought up better names. I'm sorry, I just don't see this feature as *in any way* useful, and IMHO it could easily be jetissoned to make way for better things, ie using public fn's, data and properties interchangeably. Anthony
Sep 09 2001
parent reply "Anthony Steele" <asteele nospam.iafrica.com> writes:
"Anthony Steele" <asteele nospam.iafrica.com> wrote in message
news:9ng2s3$1f7d$1 digitaldaemon.com...

 IMHO it could easily be jetissoned to make way for better things, ie using
 public fn's, data and properties interchangeably.

Further to this, language designers should learn from other people's mistakes: take a look at JWZ's essay "Why java sucks" at http://www.jwz.org/doc/java.html , in which explores what he sees as some of the flaws in Java. One point reads: The distinction between slots and methods is stupid. Doing foo.x should be defined to be equivalent to foo.x(), with lexical magic for ``foo.x = ...'' assignment. Compilers should be trivially able to inline zero-argument accessor methods to be inline object+offset loads. That way programmers wouldn't break every single one of their callers when they happen to change the internal implementation of something from something which happened to be a ``slot'' to something with slightly more complicated behavior.
Sep 09 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 The distinction between slots and methods is stupid. Doing foo.x should be
 defined to be equivalent to foo.x(), with lexical magic for ``foo.x =
 ...'' assignment. Compilers should be trivially able to inline
 zero-argument accessor methods to be inline object+offset loads. That way
 programmers wouldn't break every single one of their callers when they
 happen to change the internal implementation of something from something
 which happened to be a ``slot'' to something with slightly more
 complicated behavior.

However I believe in a principle I called myself 'context free reading'. It means that when I look at a function implementation, or even a single line of it, I can say what it will do. I don't have to look at context it occures, I don't have to look into other files that actually change the behavior of the code. Getters and Setters are nice, but they destroy 'context free reading'. Foo.a = 5; Looking at it without context can be any be anything from the obvious integer a that gets assigned the value of 10 toward to a function that will format the /dev/hda5 partition. And so far I can see and compare between language it C implemnets best context free readind, a line of code will always do what there stands. Macros of course violate C context freenes, my guess is thats the very reason why so many hate them. Also C++ violated context free reading, Like the obvious in operator overloading. I've nothing with special operaters as long I can see at the use of them that it's actually a hidden call. Concating two string with like in example: A :=: B :+: C; I've nothing against it, since it's ovious that this is not normal arithmetic. I can understand what this code will do and how the assembler will roughly look like without having to look at the classes A B and C. Also reference parameter or out paramters destroy context free reading in my sense. Someone doesn't exepect a function parameter to be usually changed. Take following C++ code: int a = 1; check(a); if (a > 0) { // do you think this will be exectuted? } Wrong, check could define it's first parameter as reference, or as out. One cannot see this in the call without looking at the implementation of check. Otherwise in c; check(&a); It's obvious on first sight that a can be altered by the callee. Thats why I for my project sticked with the reference parameter for calls. I know the compiler doesn't need it, but the programmer does, and more likely the maintainer or debugger of source somebody else written will apriciate it a lot. 'Refrence' does not mean to be exactly the same as 'address of' altough it resembles to 90% to it. The difference is the reference of a reference parameter is still the reference, altough the address of the address is something different. - Axel
Sep 09 2001
parent reply "Anthony Steele" <asteele nospam.iafrica.com> writes:
"Axel Kittenberger" <axel dtone.org> wrote in message
news:9ngeo2$1lal$1 digitaldaemon.com...

 The distinction between slots and methods is stupid. Doing foo.x should


 However I believe in a principle I called myself 'context free reading'.
 It means that when I look at a function implementation, or even a single
 line of it, I can say what it will do. I don't have to look at context it
 occures, I don't have to look into other files that actually change the
 behavior of the code. Getters and Setters are nice, but they destroy
 'context free reading'. Foo.a = 5; Looking at it without context can be

 be anything from the obvious integer a that gets assigned the value of 10
 toward to a function that will format the /dev/hda5 partition.

I have heared that argument before, and I don't buy it. There is no language construct, no language even that can save your butt if your co-programmers are malicious morons. *whenever* you use an object, with or without properties, you look at the public interface, temporarily ignore the implementation and just asume that the functions do something related to thier names & the docs. Even if you do read the implementation, you don't have an absolute guarantee that it will stay the same, just a vauge assurance that it even if the implementation changes, it will try to reach the same 'goal'. A settor invoked when you set foo.a that does some action unrelated to storing a value is just bad code. if foo.a = 5 or foo.setA(5); reformats the HD, then the public interface of that class is just wrong. If your property use destroys your ability to read what the class is doing, then you are using properties wrong. I don't think it's hard - I've seldom if ever seen anyone get it badly wrong in practice. My practical experience of using properties in Delphi for several years is that (all other things being equal & co-workers being relatively sane) that using public properties makes your code cleaner & easier to read, even if you only look at the class interface. OO programming is partly about implementation changing to better support a public interface without the users of that interface having to know about it. Properties help with this by allowing you to add or revise gettor & settor methods, but they cannot protect you from a bad interface. But then nothing can - except good design. Anthony
Sep 10 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 I have heared that argument before, and I don't buy it. There is no
 language construct, no language even that can save your butt if your
 co-programmers are malicious morons. *whenever* you use an object, with or
 without properties, you look at the public interface, temporarily ignore
 the implementation and just asume that the functions do something related
 to thier names & the docs. Even if you do read the implementation, you
 don't have an absolute guarantee that it will stay the same, just a vauge
 assurance that it even if the implementation changes, it will try to reach
 the same 'goal'.

However, it's the same with a function call, strcat() can also format the harddisk. But I will still know that this is a function call, I know that it will push stuff on the stack, it will jump else where and will return. Except the function is inlined of course, but the results are the same. However I still want to see calls on the first sight, not having them hidden behind stuff that looks like a field access, it just makes debugging in hard cases difficult. And in hardcases I mean when you're experimenting with a new hardware, that doesn't yet run 100%. In some worst case situations (and they do happen) you step down at stepping through the source assembler instruction by instruction. Until you'll find an insturction that is not handled by the hardware, because in example these two instructions in combination raise a silicon bug, or create a bus burst that breaks the memory content, reads the next instruction false into the cache or whatever. But even with O2 optimation turned on and viewing assembler in one window, while having the C source in the other, one can easily draw the lines between the two in mind. Having an call hidden behind a normal Foo.x destroy this completly. That's the reason why C was a system languange, and this it the bias it was created for, and I would expect from something that calls itself it's sucessor to handle it in the same bias, or call himself differntly. - Axel
Sep 10 2001
parent reply Russ Lewis <russ deming-os.org> writes:
Axel Kittenberger wrote:

 However, it's the same with a function call, strcat() can also format the
 harddisk. But I will still know that this is a function call, I know that
 it will push stuff on the stack, it will jump else where and will return.
 Except the function is inlined of course, but the results are the same.
 However I still want to see calls on the first sight, not having them
 hidden behind stuff that looks like a field access, it just makes debugging
 in hard cases difficult. And in hardcases I mean when you're experimenting
 with a new hardware, that doesn't yet run 100%. In some worst case
 situations (and they do happen) you step down at stepping through the
 source assembler instruction by instruction. Until you'll find an
 insturction that is not handled by the hardware, because in example these
 two instructions in combination raise a silicon bug, or create a bus burst
 that breaks the memory content, reads the next instruction false into the
 cache or whatever. But even with O2 optimation turned on and viewing
 assembler in one window, while having the C source in the other, one can
 easily draw the lines between the two in mind. Having an call hidden behind
 a normal Foo.x destroy this completly.

This really seems like a serious double-standard in your logic. You're assuming a situation where the user is doing some really serious low-level debugging of hardware. This implies someone who has a very in-depth knowledge of the system and of computers in general. At the same time, you assume that this programmer is not aware of gettors and settors in D, or if he did, he didn't take the time to look at the definition of the class. If you are doing something as low-level as you are talking, then you should not be using weird class libraries you don't know well (or that somebody is still modifying the implementation), nor should you be using a language that you are not an expert in. The whole idea of the programmer being "surprised" simply indicates that he should have used another language that he knew better...be it C, assembly, or whatever. This same argument could be used against inlined functions, macros, typedefs, or even structs. If the programmer doesn't know what the language does, he shouldn't be using it for this kind of debugging. If he doesn't understand how the compiler works, he shouldn't be trying to step through the assembly that it generated.
Sep 10 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 This really seems like a serious double-standard in your logic.  You're
 assuming a situation where the user is doing some really serious low-level
 debugging of
 hardware.  This implies someone who has a very in-depth knowledge of the
 system and of computers in general.
 
 At the same time, you assume that this programmer is not aware of gettors
 and settors in D, or if he did, he didn't take the time to look at the
 definition of the class.
 
 If you are doing something as low-level as you are talking, then you
 should not be using weird class libraries you don't know well (or that
 somebody is still modifying the implementation), nor should you be using a
 language that you are
 not an expert in.  The whole idea of the programmer being "surprised"
 simply indicates that he should have used another language that he knew
 better...be it C, assembly, or whatever.
 
 This same argument could be used against inlined functions, macros,
 typedefs, or
 even structs.  If the programmer doesn't know what the language does, he
 shouldn't be using it for this kind of debugging.  If he doesn't
 understand how the compiler works, he shouldn't be trying to step through
 the assembly that it generated.

Actually the class Foo does not need to be a standard class, and also very often you're debugging code someone else has written. It's just as you said, for getters&setters you need to know how Foo is constructed to see what it results in assembler, in the context-free paradigm you don't need to, it's paradigm, like it or not. I think it's important for a) huge projects where you can never have a global view of things b) projects more than one guy works on it, for one man projects everything changes significantly. And well one can understand C very well and be an expert in it, but you're assuming that when I in example debug the linux kernel, I also have to have a global expert-knowledge to it. Actually it's not the case, in C you can just debug the part that makes problems, without knowing surrondings, or having to look at other implementations elsewhere in the code. - Axel
Sep 10 2001
parent Russ Lewis <russ deming-os.org> writes:
I agree with you here, though it seems like a contradiction.  Your previous post
(the one I was responding to) was presuming that you were debugging *hardware*.
That's much lower level than even an OS kernel, much less complex software
written by somebody else.  IMHO, at that (hardware) level, it's reasonable to
expect that the debugging programmer knows the software intimately.
Sep 11 2001
prev sibling parent Russ Lewis <russ deming-os.org> writes:
Anthony Steele wrote:

 class Foo
 {
     int m_bar;
  public:
     int bar() { return m_bar; };
  };

<slap self> This, of course, is the (trivial) solution. However, to use it, we must change the spec a bit. The spec is that you can expose gettors and settors for a specific variable of the same name. Instead, the spec will have to say that if you declare a function that takes no arguments, then it can be called like a variable. The compiler will have to differentiate between l-values and r-values to call the right function (or to determine that no applicable function exists).
Sep 05 2001