I'm working on a generic collection class template, let's say List(T) where i'd like to be able to do something like php's late static binding. Might be best illustrated with some simplified sample code. This code compiles fine as is on dmd, but it needs a small change to be the way I want.
module main;
import std.stdio;
import std.string;
class List(T)
{
private T[] _list;
public void append(T t)
{
_list ~= t;
}
// this is where some help is needed...
public List select(bool delegate(T t) dg)
{
// auto should be whatever subclass of List(T) is calling this method.
auto result = new List!T();
foreach(t; _list)
{
if (dg(t)) result.append(t);
}
return result;
}
int opApply(int delegate(ref T) dg)
{
int result = 0;
for (int i = 0; i < _list.length; i++)
{
result = dg(_list[i]);
if (result)
break;
}
return result;
}
}
enum Gender
{
MALE,
FEMALE,
SECRET
}
class Person
{
private string _firstName;
private string _lastName;
private string _email;
private Gender _gender;
@property public string firstName() {return _firstName;}
@property public string lastName() {return _lastName;}
@property public string email() {return _email;}
@property public Gender gender() {return _gender;}
public this()
{
}
public this(string firstName, string lastName, Gender gender = Gender.SECRET, string email = "[email protected]")
{
this();
this._firstName = firstName;
this._lastName = lastName;
this._gender = gender;
this._email = email;
}
override public string toString()
{
if (email.length > 0)
{
return "%s %s <%s>".format(firstName, lastName, email);
}
else
{
return "%s %s".format(firstName, lastName);
}
}
}
class PeopleList : List!Person
{
// I would like to be able to make this: public PeopleList selectByGender(Gender gender)
public List!Person selectByGender(Gender gender)
{
return select(p => p.gender == gender);
}
}
void main(string[] args)
{
auto people = new PeopleList();
people.append(new Person("Kris", "Herlaar", Gender.MALE));
people.append(new Person("John", "Doe", Gender.MALE));
people.append(new Person("Steve", "Wozniak", Gender.MALE));
people.append(new Person("Walter", "Bright", Gender.MALE));
people.append(new Person("Amelia", "Earhart", Gender.FEMALE, null));
people.append(new Person("Susan", "Anthony", Gender.FEMALE, null));
foreach(p; people.selectByGender(Gender.FEMALE))
{
writeln(p);
}
}
How would I go about making sure that PeopleList.select would also return an instance of PeopleList instead of a List!Person so that the commented out declaration of selectByGender is correct?
I could probably mock about with Object.factory(this.classinfo.name) inside the implementation and actually get the correct type of instance for result but I figure that would not help with the declared return-type.
I'd want to make chaining methods possible so I'd need the compiler to allow me to return instances of whatever subclass is calling List(T).select I imagine it could be done with a nested template, but haven't been able to come up with anything that would compile, let alone seem elegant.
Additional info in reply to received feedback
I am aware of std.algorithm and of filter, In real life; this code does not represent an actual use-case but a thought experiment to learn more of the abilities/limits of D and its' templates.