On Wed, 22 Apr 2009 13:45:16 +0000 (UTC), dsimcha <[email protected]> wrote:
>== Quote from Jarrett Billingsley ([email protected])'s article >> On Wed, Apr 22, 2009 at 12:42 AM, dsimcha <[email protected]> wrote: >> > I'm working on porting dstats to ranges and I've run across an interestin >> g >> > problem. I've defined a range that I'm going to use for joint entropy, >> etc. >> > It's basically a tuple with some special properties. Let's say I have >> a >> > template struct: >> > >> > struct Joint(T...) { >> > T ranges; >> > } >> > >> > It's built with: >> > >> > SomeType joint(T...)(T args) { // do stuff. } >> > >> > When one of the arguments is a Joint, I want it to flatten. For exampl >> e, >> > >> > Joint!(uint[], uint[]) r1; >> > uint[] r2; >> > auto result = joint(r1, r2); >> > // result is a Joint!(uint[], uint[], uint[]), not a >> > // Joint!(Joint!(uint[], uint[]), uint[]). >> > >> > Is there an easy metaprogramming trick that I've overlooked to make stuff >> > flatten like this? >> So before reading the following solution, don't get your hopes up too >> much. There's a compiler bug that prevents it from working. >> template Tuple(T...) >> { >> alias T Tuple; >> } >> template FlattenJoint(T : Joint!(U), U...) >> { >> alias FlatJoint!(U) FlattenJoint; >> } >> template FlattenJoint(T) >> { >> alias T FlattenJoint; >> } >> template FlatJoint(T...) >> { >> static if(T.length == 0) >> alias Tuple!() FlatJoint; >> else >> alias Tuple!(FlattenJoint!(T[0]), FlatJoint!(T[1 .. $])) >> FlatJoint; >> } >> struct Joint(T...) >> { >> FlatJoint!(T) ranges; >> } >> void main() >> { >> Joint!(uint[], uint[]) r1; >> Joint!(Joint!(uint[], uint[]), uint[]) r2; >> pragma(msg, typeof(r1.ranges).stringof); >> pragma(msg, typeof(r2.ranges).stringof); >> } >> This will print out: >> (uint[], uint[]) >> (uint[]) >> The second line really should be (uint[], uint[], uint[]), but there's >> something wrong with the way DMD matches the FlattenJoint template. I >> was surprised, it does actually select the correct >> specialization(FlattenJoint(T : Joint!(U), U...), but for some reason, >> U is the empty tuple, even though T is Joint!(uint[], uint[]). >> I think it might have something to do with this bug (D2 is() >> expression, but a very similar mechanism and result): >> http://d.puremagic.com/issues/show_bug.cgi?id=1944 > >I guess I should clarify: Getting the flattened type tuple is the easy part. >He >hard part is getting the flattened parameter tuple, i.e. how do I copy all the >data over to the new Joint!(uint[], uint[], uint[]) struct in a generic way? You could do it like this: struct Joint(T...) { T ranges; } template isJoint(T) { enum isJoint = is(typeof(T.ranges)); // or whatever means you choose to identify a Joint } template JointRetType(T...) { static if (T.length) { static if (isJoint!(T[0])) alias Joint!(typeof(T[0].ranges), typeof(JointRetType!(T[1..$]).ranges)) JointRetType; else alias Joint!(T[0], typeof(JointRetType!(T[1..$]).ranges)) JointRetType; } else alias Joint!() JointRetType; } private /+ auto +/ Joint!(T) flatJoint(T...)(T args) { return Joint!(T)(args); } /+ auto +/ JointRetType!(T) joint(T...)(T args) { static if (T.length) { static if (is(typeof(T[0].ranges))) return flatJoint(args[0].ranges, joint(args[1..$]).ranges); else return flatJoint(args[0], joint(args[1..$]).ranges); } else return Joint!()(); } void main() { Joint!(uint[], uint[]) r1; uint[] r2; auto result = joint(r1, r2); static assert(is(typeof(result) == Joint!(uint[], uint[], uint[]))); } Could be optimized to eliminate excessive copying. JointRetType is necessary because you can't use 'auto' (http://d.puremagic.com/issues/show_bug.cgi?id=2863)
