On 03/28/2014 08:00 PM, H. S. Teoh wrote:
Today I ran into an interesting situation where I have a function f that
needs to return ranges of different types (but identical element types):

        auto f(A...)(A args) {
                ...
                if (someCondition)
                        return cartesianProduct(x, y)
                                .joiner;
                else
                        return cartesianProduct(x, y)
                                .joiner
                                .filter!someFilter;
        }

This obviously can't compile, because the return types are not the same.
(Note that someCondition is only known at runtime.) But abstractly
speaking, it *should* work, because the element type of the returned
range is identical.

So how would I implement something like this?


T


The following is as close as I got. I think the definite initialization checks DMD implements are horribly broken for union fields.

import std.range, std.algorithm, std.typetuple, std.traits;

template CommonElementType(T...)if(allSatisfy!(isInputRange,T)){
    alias CommonElementType = CommonType!(staticMap!(ElementType,T));
}

private template Neg(alias a){
    enum Neg(T...)=!a!T;
}

struct SumRange(T...)if(allSatisfy!(isInputRange,T)&&!is(void==CommonElementType!T)&&allSatisfy!(Neg!hasElaborateDestructor,T)&&allSatisfy!(Neg!hasElaborateCopyConstructor,T)){
    size_t tag;
    private union{ T rs=void; }
    // private this();
    @property front()@trusted{
        switch(tag){
            foreach(i,_;T) case i: return rs[i].front;
            default: assert(0);
        }
    }
    @property bool empty()@trusted{
        switch(tag){
            foreach(i,_;T) case i: return rs[i].empty;
            default: assert(0);
        }
    }
    void popFront()@trusted{
        switch(tag){
            foreach(i,_;T) case i: return rs[i].popFront();
            default: assert(0);
        }
    }
}

private T buildSum(T, size_t tag,S)(S arg)@trusted{
    T r;
    r.tag=tag;
    r.rs[tag]=arg;
    return r;
}

auto inl(T,S)(S arg)if(!hasElaborateDestructor!S&&!hasElaborateDestructor!T&&!hasElaborateCopyConstructor!S&&!hasElaborateCopyConstructor!T){
    return buildSum!(SumRange!(S,T),0)(arg);
}
auto inr(S,T)(T arg)if(!hasElaborateDestructor!S&&!hasElaborateDestructor!T&&!hasElaborateCopyConstructor!S&&!hasElaborateCopyConstructor!T){
    return buildSum!(SumRange!(S,T),1)(arg);
}

auto f(R,S)(bool condition,R x,S y){
    auto r1(){
        return cartesianProduct(x, y).map!(a=>[a.expand])
            .joiner;
    }
    auto r2(){
        return cartesianProduct(x, y).map!(a=>[a.expand])
            .joiner
            .filter!(a=>a>2);
    }
    if(condition) return r1().inl!(typeof(r2()));
    else return r2().inr!(typeof(r1()));
}

void main(){
    import std.stdio;
    writeln(f(true, [1,2,3], [4,5,6]));
    writeln(f(false, [1,2,3], [4,5,6]));
}

Reply via email to