Nifty chaining
Steven Schveighoffer
schveiguy at yahoo.com
Mon Oct 4 06:52:21 PDT 2010
One of the warts of dcollections' API is this model:
class C(V)
{
C add(V v, out bool wasAdded)
C add(V v)
}
So you have the ability to chain calls to C via:
c.add(1).add(2);
And it still supports the ability to determine if the values were added.
But the API is clunky when you *do* want to know what was added, you have
to pre-declare your variables, plus, when you are chaining, do you really
care if each individual element was added, or do you want to know the
effect of the entire expression?
In addition, I have various different add overloads, one of which takes a
dynamic array.
I had wondered, wouldn't it be nice to just have this:
C add(V[] elems...)
and that would replace add(V v) as well. But I ran into a snag, I can't
replace this function:
C add(V[] elems, out uint numAdded)
because what if V is uint?
I thought of this idea: What I really want is the ability to chain calls,
but also get the difference in length. So I built this struct, and it
actually works:
struct Chainer(T)
{
T t;
private size_t origlen;
this(T t)
{
this.t = t;
this.origlen = t.length;
}
Chainer opDispatch(string fn, Args...)(Args args) if
(is(typeof(mixin("t." ~ fn ~ "(args)")) == T))
{
mixin("t." ~ fn ~ "(args);");
return this;
}
@property int delta() const {return cast(int)(t.length - origlen);}
}
Chainer!T chain(T)(T t)
{
return Chainer!T(t);
}
So here we have a struct that allows you to chain, *plus* allows you to
get at the returned delta in length.
So you would use it like this:
auto numAdded = chain(mycollection).add(1,2,3).add(4,5,6,7).delta;
I originally wanted to just have each function that wanted to use chain
calling return a Chainer!(typeof(this)), so you would use it like:
auto numAdded = mycollection.add(1,2,3).add(4,5,6,7).delta;
but this doesn't work for covariance. And I use covariance everywhere to
allow chaining no matter what the derived type is. Although all
dcollections classes are final, I use interfaces, and those could not be
covariant (e.g. cannot implicitly cast Chainer!LinkList to Chainer!List).
I also originally wanted to allow implicit casting of Chainer!T to int
which would return the given delta, but this doesn't work. Adding 'alias
delta this;' results in the error:
Error: no property 'add' for type 'int'
Which seems to suggest that the compiler will not try to use opDispatch
when an alias this is present. Does that sound like a bug or expected
behavior?
Anyhow, the next version of dcollections will likely have these features.
I wonder if a more generic "Chainer" type could be useful in Phobos.
Basically, one which either accumulates some data on each chained call, or
which determines a delta at the end.
-Steve
More information about the Digitalmars-d
mailing list