www.digitalmars.com         C & C++   DMDScript  

D - Alternative syntax for property getters

reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
While properties are still on Walters to-do list, here I come with one more
attempt to revolution... :-) Hope I'm not annoying anyone.

Since most property getters will probably just return the value of a data
member, there's gonna be lots of:

class MyClass
{
private:
  int m_MyProperty;
public:
  int MyProperty() { return m_MyProperty; }
  void MyProperty(int aValue) { m_MyProperty =
do_whatever_it_takes(aValue); }
}
Talking about the property getter, that's code that will _almost_ surely be
inlined by the compiler; still, it does _look_ as if a function call is
actually going to happen, and requires the compiler to recognize the code as
inline-able. Wouldn't it be easier for both the programmer and the compiler
if there was a property getter simplified form like the following?

class MyClass
{
private:
  int m_MyProperty;
public:
  int MyProperty() = m_MyProperty; // Here it is
  void MyProperty(int aValue) { m_MyProperty =
do_whatever_it_takes(aValue); }
}

That would make it clear, both to the reader and the compiler, that reading
the property _is_ actually reading the data member. Less typing, a bit more
work for the parser maybe, but less work for the optimizer.

Any feedback greatly appreciated.

Ric
Aug 04 2003
parent reply Ilya Minkov <midiclub 8ung.at> writes:
Riccardo De Agostini wrote:
 While properties are still on Walters to-do list, here I come with one more
 attempt to revolution... :-) Hope I'm not annoying anyone.
No, not at all, i was just waiting for someone to make this kind of discussion started! It's a hot topic to the library design.
 Since most property getters will probably just return the value of a data
 member, there's gonna be lots of:
[snip!]
 That would make it clear, both to the reader and the compiler, that reading
 the property _is_ actually reading the data member. Less typing, a bit more
 work for the parser maybe, but less work for the optimizer.
Well, it would be really a *very* small revolution. :) You must consider, that for getters and setters, function bodies have to be generated anyway. What for? Inheritance. I don't see how your idea would simplify the compiler. Most probably not. But what makes code more readable, is definately worth it. In Delphi, a more wordy definition is used. IIRC something along the lines of: property MyName: TType read GetName write SetName; And now comes the magic part: both the identifier after the read, and the one after the write keyword, may be either a member function (a method), or the storage field! The only disadvantage is, that it takes up one more line in worst case: if you use both non-trivial acessor and setter. Advantages are numerous: * you can search for properties by a keyword, and distinguish them on the first look; * where a field is specified instead of a method, a corresponding trivial method is generated automatically; * in D, the advantage can be exploted further, by the possibility to define getter and setter functions inline, right within the property statement! Any other ideas? -i.
Aug 04 2003
parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Ilya Minkov" <midiclub 8ung.at> ha scritto nel messaggio
news:bgln9f$1nrq$1 digitaldaemon.com...
 Riccardo De Agostini wrote:
 Well, it would be really a *very* small revolution. :)
Shall I start signing as "El Dhe"? :-)
 You must consider, that for getters and setters, function bodies have to
 be generated anyway. What for? Inheritance. I don't see how your idea
 would simplify the compiler. Most probably not. But what makes code more
 readable, is definately worth it.
The compiler would know in advance that the getter is to be inlined; maybe it would be easier for the optimizer to load the data member in whatever register is available and fits best at call time, instead of just generating a "fake" function body which always uses AX / EAX. This is only loud thinking, since I must admit I know nearly nothing about compiler theory; but it seems practicable at first sight.
 In Delphi, a more wordy definition is used. IIRC something along the
 lines of:

 property MyName: TType read GetName write SetName;

 And now comes the magic part: both the identifier after the read, and
 the one after the write keyword, may be either a member function (a
 method), or the storage field!
Guess where my idea came from... ;-) Delphi properties cannot be virtual; "read" and "write" methods can, though. Consequently, if you define a property getter as a direct access to a data member, it cannot be overridden. IMHO this would be correct in D too: no "virtual int MyProperty() = m_MyProperty". Or maybe let it be a D'ified version of Delphi's properties: // "common" should be "static" currently... but hope is the last to die... :-) // Also, read "constructor" as "this" and "destructor" as "~this". class MyClass { private: common ulong m_Count; private: int m_MyProperty; private: virtual int SetMyProperty(int aValue) { do_whatever_it_takes(aValue); } public: constructor() { ++m_Count; } destructor() { --m_Count; } public: common property ulong Count get m_Count; // read-only property int MyProperty get m_MyProperty set SetMyProperty; }
   * in D, the advantage can be exploted further, by the possibility to
 define getter and setter functions inline, right within the property
 statement!
Aw, I like that one!!! // "common" should be "static" currently... but hope is the last to die... :-) // Also, read "constructor" as "this" and "destructor" as "~this". class MyClass { private: common ulong m_Count; private: int m_MyProperty; public: constructor() { ++m_Count; } constructor(int aValue) { constructor(); MyProperty = aValue; } destructor() { --m_Count; } public: common property ulong Count get m_Count; // read-only property int MyProperty get m_MyProperty set { do_whatever_it_takes(aValue); } } This leaves me with a question mark about virtual methods, but I'm sure it can be worked out. Any ideas anyone? Ric
Aug 04 2003
parent reply Ilya Minkov <midiclub 8ung.at> writes:
Riccardo De Agostini wrote:

Hm... i didn't know delphi properties had to resolve statically... What 
a shame on me!

Anyway, we'd have virtual in D.

 This leaves me with a question mark about virtual methods, but I'm sure it
 can be worked out. Any ideas anyone?
In D, all object methods are virtual. Just that in the optimised builds, the compiler would be able to figure out which of them are final, and thus be able to inline them. BTW, inlining is done on approximately C code level, there are no registers marked yet. That's why it's such an effective optimisation: after common subexpression removal and assignment flow optimisations, the code may simplify greatly, thus matching hand-optimised code in quality. -i.
Aug 04 2003
parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Ilya Minkov" <midiclub 8ung.at> ha scritto nel messaggio
news:bglqbf$1qou$1 digitaldaemon.com...
 In D, all object methods are virtual. Just that in the optimised builds,
 the compiler would be able to figure out which of them are final, and
 thus be able to inline them.
Mmmh... do you mean that, given my last code example, we can just redefine the property in a subclass and let the compiler do the dirty work? That's great! Now, what if I want to redefine a read-only method as read-write, or vice versa? And if I want to define abstract getters / setters? Let's see if there can be a syntax for all this, possibly one that makes sense... // "common" should be "static" currently... but hope is the last to die... :-) // Also, read "constructor" as "this" and "destructor" as "~this". class MyBaseClass { private: common ulong m_Count; private: int m_MyProperty; public: constructor() { ++m_Count; } constructor(int aValue) { constructor(); MyProperty = aValue; } destructor() { --m_Count; } public: common property ulong Count get m_Count; // read-only property int MyProperty get m_MyProperty set { do_whatever_it_takes(aValue); } } // I want MyDerivedClass to handle setting MyProperty // internally, so it must be read-only. class MyDerivedClass: MyBaseClass { public: constructor() { super(some_initial_value); } public: property int MyProperty set 0; } In this example, MyDerivedClass does not redefine MyProperty's getter, so it remains the same, while the setter is defined as not implemented for this class. A "notimpl" keyword instead of 0 would be better, probably. While wandering in the mists of my compiler ignorance, I suppose that: - an unimplemented getter / setter would translate as a NULL pointer in the vtable; - the RTL should be smart enough as to throw an "unimplemented" exception instead of going straight to page fault, in the case of an unimplemented getter / setter being called via a base class reference (while calling it via a reference to the "unimplementing" class could be trapped at compile time, IF no further-derived classes reimplement it) - the compiler should always make room for both getter and setter every time a property is defined, in case a derived class adds a setter where its base class had none. Of course, a really cool optimizer could even strip never-implemented getters / setters from vtables, but I guess this would be no picnic...
 BTW, inlining is done on approximately C code level, there are no
 registers marked yet. That's why it's such an effective optimisation:
 after common subexpression removal and assignment flow optimisations,
 the code may simplify greatly, thus matching hand-optimised code in
quality. Thanks for enlightening me about that. As I said, compiler theory is voodoo to me. Ric
Aug 04 2003
parent reply Ilya Minkov <midiclub 8ung.at> writes:
Riccardo De Agostini wrote:

 Mmmh... do you mean that, given my last code example, we can just redefine
 the property in a subclass and let the compiler do the dirty work? That's
 great!
That's how it's intended.
 Now, what if I want to redefine a read-only method as read-write, or vice
 versa? And if I want to define abstract getters / setters? Let's see if
 there can be a syntax for all this, possibly one that makes sense...
Defining the property as read-only or write-only is probably best done by leaving out the set or get clause. It should be distinguished between defining getter or setter without an implementation and defining no getter or setter. For the first case, the class would become abstract, and current declaration syntax for pure virtual functions should be adapted. (is there one? i didn't find one in the spec!) Implicitly, a property defines 2 virtual functions, if used both with get and set clauses. What that means, is that in derived classes, you can only leave a property access as it was, or make it both read- and write-enabled, by adding another declaration clause, and implicitly by defining another function. It should be impossible to undefine a setter as you have proposed it, else calling using class is inherently unsafe. Turn to any OO book for a motivation why you cannot leave undefined virtual functions in classes which are to be instantiated, or ask me and i'll explain as soon as i have some time.
 In this example, MyDerivedClass does not redefine MyProperty's getter, so it
 remains the same, while the setter is defined as not implemented for this
 class. A "notimpl" keyword instead of 0 would be better, probably.
Why not use null keyword? Then again: this example is illegal in OO terms!
 While wandering in the mists of my compiler ignorance, I suppose that:
 - an unimplemented getter / setter would translate as a NULL pointer in the
 vtable;
A class with NULLs in the VTable cannot be safely instantiated, because other functions of this class probably rely on having each function implemented!
 - the RTL should be smart enough as to throw an "unimplemented" exception
 instead of going straight to page fault, in the case of an unimplemented
 getter / setter being called via a base class reference (while calling it
 via a reference to the "unimplementing" class could be trapped at compile
 time, IF no further-derived classes reimplement it)
If you *really* think you need to leave a getter or setter unimplemented, care to provide a stub implemntation which throws an exception -- but before you do it and break your code think again. Maybe you need to declare a more restricted access in the parent? Then you can still widen it in further derived types.
 - the compiler should always make room for both getter and setter every time
 a property is defined, in case a derived class adds a setter where its base
 class had none. Of course, a really cool optimizer could even strip
 never-implemented getters / setters from vtables, but I guess this would be
 no picnic...
It should make room for what you declare. And what you declare, you must somewhere define.
 Thanks for enlightening me about that. As I said, compiler theory is voodoo
 to me.
For me, optimisations done in the backend are black magic. :) I just know they can do amazing things, like optimise out the whole stupidity which comes from template expansion in C++... But what a frontend does, is fairly clear, at least in such a language as D which aviods too much complication. -i.
Aug 05 2003
parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Ilya Minkov" <midiclub 8ung.at> ha scritto nel messaggio
news:bgpgu7$2ens$1 digitaldaemon.com...
 Implicitly, a property defines 2 virtual functions, if used both with
 get and set clauses. What that means, is that in derived classes, you
 can only leave a property access as it was, or make it both read- and
 write-enabled, by adding another declaration clause, and implicitly by
 defining another function. It should be impossible to undefine a setter
 as you have proposed it, else calling using class is inherently unsafe.
I see your point and it's a good one, from a pragmatic OO point of view; there are, however, cases where being able to turn an inherited property from read/write to read-only can be useful.
 Turn to any OO book for a motivation why you cannot leave undefined
 virtual functions in classes which are to be instantiated, or ask me and
 i'll explain as soon as i have some time.
Too kind of you, really... <g> OK, so let's say that if I want to turn an inherited property to read-only I can always override the setter with one that just raises an exception. That should make both of us happy: no NULLs in the VTable, *and* I can break, crush and disintegrate my own code, be it perhaps for the mere fun of it. :-) All of this, unfortunately, is going to remain in the field of pure theory, however, because nobody seems to have noticed this discussion, apart from the two of us.
 It should make room for what you declare. And what you declare, you must
 somewhere define.
Consider a base class that declares a read-only property (no setter); then, two or more distinct derived classes extend the property to read-write. Woudn't we potentially end up having the address of the setter function at different points in the VTable? Or is that no problem because, being the property read-only in the base class, we cannot call the setter via a base-class reference anyway? Ric
Aug 08 2003
next sibling parent Ilya Minkov <midiclub 8ung.at> writes:
Riccardo De Agostini wrote:

 I see your point and it's a good one, from a pragmatic OO point of
 view; there are, however, cases where being able to turn an inherited
 property from read/write to read-only can be useful.
There are such cases even with usual virtual functions. :)
 OK, so let's say that if I want to turn an inherited property to
 read-only I can always override the setter with one that just raises
 an exception. That should make both of us happy: no NULLs in the
 VTable, *and* I can break, crush and disintegrate my own code, be it
 perhaps for the mere fun of it. :-)
HARD ROCK :>
 All of this, unfortunately, is going to remain in the field of pure
 theory, however, because nobody seems to have noticed this
 discussion, apart from the two of us.
Walter reads everything. Give him time.
 Consider a base class that declares a read-only property (no setter);
 then, two or more distinct derived classes extend the property to
 read-write. Woudn't we potentially end up having the address of the
 setter function at different points in the VTable?
Sure.
 Or is that no problem because, being the property read-only in the
 base class, we cannot call the setter via a base-class reference
 anyway?
Exactly, there is no way to access this setter. Now, here comes my FINAL PROPOSAL. Walter should simply decide on how these acessor functions are to be called. They can be called with a field name, as in the original plan, or with getField and setField. This is easy to do -- whenever a compiler detects a read from or a write to a field which isn't declared or is inacessible (i.e. private), it should go and generate a function call in that place. Then we can write this function definitions directly in the class, and be happy for the start. Then someday later, an alternative, greppable and legible syntax can be bolted on. I would recommend getField and setField or something along these lines for function names, since it can be searched for. Maybe propertyField for both? Ah, another thing: obviously this name rewsolution and propoerty things should not only apply to classes, but also to structs. Just that it is obvious that structs have no virtuals. How about me hacking the frontend to implement them? I'll give it a try next days. It's a pity we don't have just *some* backend to test it with! We need one of the folowing badly: * a GCC port * an integrated backport of DLI with some linkable output * interpreter/VM? * Walter: maybe we could have some crippled version of your backend as DLL? -i.
Aug 08 2003
prev sibling parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Riccardo De Agostini" <riccardo.de.agostini email.it> wrote in message
news:bh0do7$2re9$1 digitaldaemon.com...
 "Ilya Minkov" <midiclub 8ung.at> ha scritto nel messaggio
 news:bgpgu7$2ens$1 digitaldaemon.com...
 Implicitly, a property defines 2 virtual functions, if used both with
 get and set clauses. What that means, is that in derived classes, you
 can only leave a property access as it was, or make it both read- and
 write-enabled, by adding another declaration clause, and implicitly by
 defining another function. It should be impossible to undefine a setter
 as you have proposed it, else calling using class is inherently unsafe.
I see your point and it's a good one, from a pragmatic OO point of view; there are, however, cases where being able to turn an inherited property from read/write to read-only can be useful.
I can see that read to read/write would be valid, like java allows a sub class to make a method more public (not less) it would be pointless for a subclass to define access to a member as more restrictive than its base class (all you have to do is cast or pass as base and you regain access).
 Turn to any OO book for a motivation why you cannot leave undefined
 virtual functions in classes which are to be instantiated, or ask me and
 i'll explain as soon as i have some time.
Too kind of you, really... <g>
with a dynamic lang (like Java where base classes can change) so calls to super might be missing you do need a default (can all be the same throw an exception) function, but on a statically compiled lang the compiler should be able to tell that your attempting to either create an instance of an abstract class or call a method that can never be (super.method() either does nothing or causes a compiler error)
 OK, so let's say that if I want to turn an inherited property to read-only
I
 can always override the setter with one that just raises an exception.
That
 should make both of us happy: no NULLs in the VTable, *and* I can break,
 crush and disintegrate my own code, be it perhaps for the mere fun of it.
 :-)
you would have to (see above about making access less restrictive not more in sub classes).
 All of this, unfortunately, is going to remain in the field of pure
theory,
 however, because nobody seems to have noticed this discussion, apart from
 the two of us.
other are reading this, ....
 It should make room for what you declare. And what you declare, you must
 somewhere define.
Consider a base class that declares a read-only property (no setter);
then,
 two or more distinct derived classes extend the property to read-write.
 Woudn't we potentially end up having the address of the setter function at
 different points in the VTable? Or is that no problem because, being the
 property read-only in the base class, we cannot call the setter via a
 base-class reference anyway?
if you have a reference to the base class you would have to cast (thus check it was an instance of derived) to write to the property. if the properties where in the vtbl this is no different from class base { void set_bla( T newval ); } class devr : base { T get_bla(); } you would not consider calling get_bla() on an instance of base, the getter and setters will be different entries anyway (unless you feel that it should be implemented as T & get_set( T & newval, boolean get ) !!! [I don't] but like methods, because D is a statically compiled lang the compiler is free to not put methods that are overridden into the vtbl, and I would assume it would do the same for getters/setter in the above case there are 2 derived classes that had setters but did not re-implement the getter then the getter would not need to be virtual, the setter might have to be, but only if one was derived again
Aug 08 2003
parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
(This is also to reply to Ilya's message)

"Mike Wynn" <mike.wynn l8night.co.uk> ha scritto nel messaggio
news:bh0rbm$6nr$1 digitaldaemon.com...

 other are reading this, ....
[Ilya specifically mentioned Walter] That's good, because properties are among the things D more badly needs to have implemented soon, so some brainstorming about them could help Walter get the job done _and_ have a life, a family, do some payed-for work, etc. :-) (That is, as long as there's someone like the two of you, ready to catch me and beat me when I get delirious :-) No, really, guys, I don't feel beaten at all - I think I've learnt something and am grateful to you). Ric
Aug 25 2003