June 11, 2021

I am needing pairs of specific nested classes with already-coded collection management; eg: classComputers having nested classComputer as following (as seen by client code that is):

  • classComputer ← objComputers.add("ID1", "workstation # 1")
  • classComputer ← objComputers.add("ID2", "workstation # 2")
  • classComputer ← objComputers.add("ID3", "workstation # 3")
  • classComputer ← objComputers.first
  • classComputer ← objComputers.last
  • classComputer ← objComputers[2].previous
  • classComputer ← objComputers[2].next
  • string ← objComputers[2].name
  • string ← objComputers["ID2"].name
  • classComputer ← objComputers["ID2"].next
  • classComputer ← objComputers["ID2"].previous
  • size_t ← objComputers.count
  • bool ← objComputers.empty
  • bool ← objComputers.remove("ID3")
  • bool ← objComputers.removeAll()
  • classComputers ← objComputers.filterByWhatever("...") ... extended functionality like this specifically implemented on-demand by client code

... so classComputers should inherit the collection management infrastructure (ie: the store itself and associated properties/methods to handle it) while classComputer should inherit collection access with this.outer (or the like) ... now let's see my implementation:

abstract public class classCollectionItems(
   typeItems,
   typeItem
   ) {

   /// (1) given type for collection items; eg: classComputers
   /// (2) given type for collection item;  eg: classComputer

   typeItems lhs;
   typeItems rhs;

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

      int lintResult = 0; /// must find a better name

		foreach (lobjItem; items) { /// looping over the computers starting in current node

			lintResult = dg(lobjItem); /// passing single object to the loop body

			if (lintResult != 0) { break; }

		}

      if (lintResult != 0 && lhs ! is null) { lintResult = lhs.opApply(dg); } /// recursing child nodes
      if (lintResult != 0 && rhs ! is null) { lintResult = rhs.opApply(dg); } /// recursing child nodes

      return lintResult;

   }

   final public typeItem add(in string lstrID) { return false; };
   final public bool remove(in string lstrID) { return false; };
   final public bool removeAll() { this.items.length = cast(size_t) 0; return this.items.length == cast(size_t) 0; };

   final public @property const bool empty() { return this.items.empty; }
   final public @property const size_t count() { return this.count1; }
   final public @property const size_t count0() { return this.items.empty ? cast(size_t) 0 : this.items.length - cast(size_t) 1; }
   final public @property const size_t count1() { return this.items.empty ? cast(size_t) 0 : this.items.length; }

   final public @property typeItem first() { return null; }
   final public @property typeItem last() { return null; }

   abstract public class classCollectionItem {

      private size_t pintPosition0 = cast(size_t) 0; /// keep in mind that array positions are zero-based

      final public @property const size_t position0() { return this.pintPosition0; }
      final public @property const size_t position1() { return this.pintPosition0 + cast(size_t) 1; }

      final public @property const size_t countAbove() { return this.outer.items.empty ? cast(size_t) 0 : this.pintPosition0; }
      final public @property const size_t countBelow() { return this.outer.items.empty ? cast(size_t) 0 : this.outer.length - this.pintPosition0 - cast(size_t) 1; }

      /// eg: for position0=0 → countAbove=0=(position0=0) & countBelow=2=(length=3)-(position0=0)-1
      /// eg: for position0=1 → countAbove=1=(position0=1) & countBelow=1=(length=3)-(position0=1)-1
      /// eg: for position0=2 → countAbove=2=(position0=2) & countBelow=0=(length=3)-(position0=2)-1

      final public @property typeItem first() { return this.outer.items.empty ? null : this.outer.items[cast(size_t) 0]; }
      final public @property typeItem previous() { return this.outer.items.empty || this.countAbove == cast(size_t) 0 ? null : this.outer.items[this.pintPosition0 - cast(size_t) 1]; }
      final public @property typeItem next() { return this.outer.items.empty || this.countBelow == cast(size_t) 0 ? null : this.outer.items[this.position0 + cast(size_t) 1]; }
      final public @property typeItem last() { return this.outer.items.empty ? null : this.outer.items[this.outer.items.length - cast(size_t) 1]; }

   }

}

... so far so good, now, when I code my specific classes I am not sure which is the correct syntax to inherit the base pair:

public class classComputers : classCollectionItems!(classComputers, classComputers.classComputer) { /// seems logical to me

   public class classComputer {}
   public class classComputer : classCollectionItem {}
   public class classComputer : classCollectionItems!(classComputers, classComputers.classComputer).classCollectionItem {}

}