www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - is it possible to have a default property for any given class ?

reply someone <someone somewhere.com> writes:
Consider the following code:

```d
class classComputer {

    private string pstrName;

    final  property string name() { return this.pstrName; }
    final  property void name(in string lstrName) { this.pstrName 
= lstrName; }

    this(
       string lstrComputerName
       ) {

       this.pstrName = lstrComputerName;

    }

}

class classComputers {

    classComputers lhs;
    classComputers rhs;

    int opApply(int delegate(classComputers) dg) { /// boilerplate 
code to handle the class's default collection

       if (lhs && lhs.opApply(dg)) return 1;
       if (dg(this)) return 1;
       if (rhs && rhs.opApply(dg)) return 1;
       return 0;

    }

    public classComputer[] computers; /// how can I tag this as 
the default property ?

}

void main (

    ) {

    classComputers lobjComputers = new classComputers;
    lobjComputers.computers ~= new classComputer("dell");
    lobjComputers.computers ~= new classComputer("ibm");
    lobjComputers.computers ~= new classComputer("apple");
    lobjComputers.computers[1].name = r"lenovo";

    foreach(lobjComputer; lobjComputers.computers) { 
writeln(lobjComputer.name); }

    ///foreach(lobjComputer; lobjComputers) { 
writeln(lobjComputer.name); } /// with default property (if 
possible)

}
```

The above code works correctly, however, avoiding the redundancy 
of lobjComputers.computers will be a plus.

Also tell me if the collection is implemented the right way, it 
is my first code using the opApply() delegate which I don't 
deeply understand for the time being.
Jun 07
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jun 07, 2021 at 03:26:27PM +0000, someone via Digitalmars-d-learn wrote:
 Consider the following code:
 
 ```d
 class classComputer {
[...]
 }
 
 class classComputers {
 
    classComputers lhs;
    classComputers rhs;
 
    int opApply(int delegate(classComputers) dg) { /// boilerplate code to
 handle the class's default collection
 
       if (lhs && lhs.opApply(dg)) return 1;
       if (dg(this)) return 1;
       if (rhs && rhs.opApply(dg)) return 1;
       return 0;
 
    }
 
    public classComputer[] computers; /// how can I tag this as the default
 property ?
alias computers this;
 
 }
 
 void main (
 
    ) {
 
    classComputers lobjComputers = new classComputers;
    lobjComputers.computers ~= new classComputer("dell");
    lobjComputers.computers ~= new classComputer("ibm");
    lobjComputers.computers ~= new classComputer("apple");
    lobjComputers.computers[1].name = r"lenovo";
 
    foreach(lobjComputer; lobjComputers.computers) {
 writeln(lobjComputer.name); }
 
    ///foreach(lobjComputer; lobjComputers) { writeln(lobjComputer.name); }
 /// with default property (if possible)
 
 }
 ```
 
 The above code works correctly, however, avoiding the redundancy of
 lobjComputers.computers will be a plus.
 
 Also tell me if the collection is implemented the right way, it is my
 first code using the opApply() delegate which I don't deeply
 understand for the time being.
It's very simple. Whenever some non-array object appears on the right side of a foreach() statement, the compiler looks for a method on the object called .opApply. If it exists, the loop body is passed to that method as a delegate. IOW: // This: foreach (item; myCollection) { /* loop body here */ } // Gets translated to this: myCollection.opApply((item) { /* loop body here */ }); Given that, your .opApply method doesn't really do what you want. It should instead be written like this: // N.B.: dg is NOT the type of the collection, but the // individual item you want to iterate over. int opApply(int delegate(classComputer) dg) { // Loop over the computers in the current node first foreach (computer; computers) { // Pass single item to loop body. auto ret = dg(computer); // N.B.: do NOT assume a non-zero return value // will always be 1; it may not be if your loop // body contains `break` or `continue`. if (ret) return ret; } // Now recurse child nodes if (lhs) { auto ret = lhs.opApply(dg); if (ret) return ret; // again, don't assume it will be 1 } if (rhs) { auto ret = rhs.opApply(dg); if (ret) return ret; // again, don't assume it will be 1 } return 0; } T -- Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
Jun 07
parent someone <someone somewhere.com> writes:
On Monday, 7 June 2021 at 15:55:36 UTC, H. S. Teoh wrote:

Thanks for your reply. It was very illustrating.

 It's very simple. Whenever some non-array object appears on the 
 right side of a foreach() statement, the compiler looks for a 
 method on the object called .opApply. If it exists, the loop 
 body is passed to that method as a delegate.  IOW:

```d
 	// This:
 	foreach (item; myCollection) {
 		/* loop body here */
 	}

 	// Gets translated to this:
 	myCollection.opApply((item) { /* loop body here */ });
```

 Given that, your .opApply method doesn't really do what you 
 want. It should instead be written like this:

 	// N.B.: dg is NOT the type of the collection, but the
 	// individual item you want to iterate over.

```d
 	int opApply(int delegate(classComputer) dg)
 	{
 		// Loop over the computers in the current node first
 		foreach (computer; computers) {
 			// Pass single item to loop body.
 			auto ret = dg(computer);

 			// N.B.: do NOT assume a non-zero return value
 			// will always be 1; it may not be if your loop
 			// body contains `break` or `continue`.
 			if (ret) return ret;
 		}

 		// Now recurse child nodes
 		if (lhs) {
 			auto ret = lhs.opApply(dg);
 			if (ret) return ret; // again, don't assume it will be 1
 		}
 		if (rhs) {
 			auto ret = rhs.opApply(dg);
 			if (ret) return ret; // again, don't assume it will be 1
 		}
 		return 0;
 	}
```
For the sake of clarity (to me) and, given I never liked code with multiple returns, I rewrote your code (providing I am not introducing flaws) as following: ```d class classComputers { classComputers lhs; classComputers rhs; int opApply(int delegate(classComputer) dg) { /// boilerplate code to handle the class's default collection int lintResult = 0; foreach (lobjComputer; computers) { /// looping over the computers starting in current node lintResult = dg(lobjComputer); /// passing single object to the loop body if (lintResult != 0) { break; } } if (lintResult != 0 && lhs) { lintResult = lhs.opApply(dg); } /// recursing child nodes if (lintResult != 0 && rhs) { lintResult = rhs.opApply(dg); } /// recursing child nodes return lintResult; } public classComputer[] computers; alias computers this; /// ie: default property } ``` It seems it works perfectly well. Also, your: ```d alias computers this; ``` was very clever, indeed :)
Jun 07
prev sibling parent reply Jack <jckj33 gmail.com> writes:
On Monday, 7 June 2021 at 15:26:27 UTC, someone wrote:
 Consider the following code:

 ```d
 class classComputer {

    private string pstrName;

    final  property string name() { return this.pstrName; }
    final  property void name(in string lstrName) { 
 this.pstrName = lstrName; }

    this(
       string lstrComputerName
       ) {

       this.pstrName = lstrComputerName;

    }

 }

 class classComputers {

    classComputers lhs;
    classComputers rhs;

    int opApply(int delegate(classComputers) dg) { /// 
 boilerplate code to handle the class's default collection

       if (lhs && lhs.opApply(dg)) return 1;
       if (dg(this)) return 1;
       if (rhs && rhs.opApply(dg)) return 1;
       return 0;

    }

    public classComputer[] computers; /// how can I tag this as 
 the default property ?

 }

 void main (

    ) {

    classComputers lobjComputers = new classComputers;
    lobjComputers.computers ~= new classComputer("dell");
    lobjComputers.computers ~= new classComputer("ibm");
    lobjComputers.computers ~= new classComputer("apple");
    lobjComputers.computers[1].name = r"lenovo";

    foreach(lobjComputer; lobjComputers.computers) { 
 writeln(lobjComputer.name); }

    ///foreach(lobjComputer; lobjComputers) { 
 writeln(lobjComputer.name); } /// with default property (if 
 possible)
I think you meant to implement ranges? you can implement in the way you wanted: ```foreach(lobjComputer; lobjComputers)``` but the recommend approach is to get this array through the array index operator. It would go like this: ```d class classComputer { private string pstrName; final string name() { return this.pstrName; } final void name(in string lstrName) { this.pstrName = lstrName; } this(string lstrComputerName = null) { this.pstrName = lstrComputerName; } classComputer lhs; classComputer rhs; int opApply(int delegate(classComputer) dg) { /// boilerplate code to handle the class's default collection if (lhs && lhs.opApply(dg)) return 1; if (dg(this)) return 1; if (rhs && rhs.opApply(dg)) return 1; return 0; } public classComputer[] computers; /// how can I tag this as the default property ? auto opIndex() nothrow { return Range(computers); } protected static struct Range { private classComputer[] a; auto front() { return a[0]; } auto back() { return a[$ - 1]; } void popFront() { a = a[1 .. $]; } bool empty() { return a.length == 0; } size_t opDollar() { return a.length; } auto opSlice(size_t start, size_t end) { return a[start .. end]; } } } void main () { import std.stdio : writeln; auto lobjComputers = new classComputer; lobjComputers.computers ~= new classComputer("dell"); lobjComputers.computers ~= new classComputer("ibm"); lobjComputers.computers ~= new classComputer("apple"); lobjComputers.computers[1].name = r"lenovo"; foreach(lobjComputer; lobjComputers[]) { writeln(lobjComputer.name); } ///foreach(lobjComputer; lobjComputers) { writeln(lobjComputer.name); } /// with default property (if possible) } ```
 }
 ```
Jun 07
parent someone <someone somewhere.com> writes:
On Monday, 7 June 2021 at 16:10:08 UTC, Jack wrote:
 I think you meant to implement ranges? you can implement in the 
 way you wanted: ```foreach(lobjComputer; lobjComputers)``` ...
Thanks for your reply ! I am aware that ranges are one of D's gems, I did take an overview of them last night or the night before but did not get immersed in them too much. Will look at them. The first thing that came to mind was to implement them as a collection as I always did on other languages, but I suppose ranges could be an alternative.
 ... but the recommend approach is to get this array through the 
 array index operator
When you say so, you mean you prefer arrays over ranges or, that you disagree with the recommended approach for whatever reasons ? Could you elaborate ?
Jun 07