Thread overview
Alternative syntax for property getters
Aug 04, 2003
Ilya Minkov
Aug 04, 2003
Ilya Minkov
Aug 06, 2003
Ilya Minkov
Aug 08, 2003
Ilya Minkov
Aug 08, 2003
Mike Wynn
August 04, 2003
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


August 04, 2003
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.

August 04, 2003
"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


August 04, 2003
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.

August 04, 2003
"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


August 06, 2003
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.

August 08, 2003
"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


August 08, 2003
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.

August 08, 2003
"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


August 25, 2003
(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