Struct Flattening

Max Samukha samukha at voliacable.com.removethis
Thu Apr 23 01:45:01 PDT 2009


On Wed, 22 Apr 2009 13:45:16 +0000 (UTC), dsimcha <dsimcha at yahoo.com>
wrote:

>== Quote from Jarrett Billingsley (jarrett.billingsley at gmail.com)'s article
>> On Wed, Apr 22, 2009 at 12:42 AM, dsimcha <dsimcha at yahoo.com> 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)



More information about the Digitalmars-d mailing list