Thread overview
is it possible to have a default property for any given class ?
Jun 07, 2021
someone
Jun 07, 2021
H. S. Teoh
Jun 07, 2021
someone
Jun 07, 2021
Jack
Jun 07, 2021
someone
June 07, 2021

Consider the following code:

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.

June 07, 2021
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
June 07, 2021

On Monday, 7 June 2021 at 15:26:27 UTC, someone wrote:

>

Consider the following code:

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:

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)

}
>

}

June 07, 2021

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:

	// 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;
	}

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:

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:

alias computers this;

was very clever, indeed :)

June 07, 2021

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 ?