I came up with a library solution that isn't pretty ;/

I offer it up to the gods, but being gods, they probably don't care.


template EnumMapper(alias func, string[] args, eT...)
{
import std.meta, std.typecons, std.traits, std.string, std.algorithm, std.array, std.conv;
        
private auto recSwitch(string[] args, int depth, alias N, T...)(string[] attrs = null)
        {               
                string str;
                auto tab = replicate("\t", depth);
                static if (T.length == 0)
                {
                        string at;
                        foreach(k, a; args)
                        {
at ~= "cast(Parameters!("~func~"!("~attrs.join(", ")~"))["~to!string(k)~"])"~a;
                                if (k < args.length-1) at ~= ", ";
                        }
return tab~"\treturn "~func~"!("~attrs.join(", ")~")("~at~");\n";
                }
                else
                {
                                                
str ~= tab~"switch("~N[0]~")\n"~tab~"{\n"~tab~"\tdefault: break;\n";
                        foreach(v; __traits(allMembers, T[0]))
                        {                               
mixin(`enum attr = __traits(getAttributes, T[0].`~v~`).stringof[6..$-1].strip();`);
                                static if (attr != "")
                                {
                                        str ~= tab~"\t"~"case "~v~":\n";        
                          
                                        attrs ~= attr[1..$-1];
str ~= recSwitch!(args, depth + 2 , N[1..$], T[1..$])(attrs);
                                        attrs = attrs[0..$-1];
                                        str ~= tab~"\t\tbreak;\n";
                                        
                                }
                        }
                        str ~= tab~"}\n";
                        
                        return str;
                }
        }

        private auto genMapper(string[] args, alias N, T...)()
        {
                string str;
                foreach(e; AliasSeq!(eT[0..eT.length/2]))
                        str ~= "with("~e.stringof~") ";
                auto code = recSwitch!(args, 0, N, T)();
                return str~"\n"~code;
        }

        auto EnumMapper()
        {
return "import std.traits;\n"~genMapper!(args, [eT[eT.length/2..$]], eT[0..eT.length/2])();
        }
}


Because D only half-assley implements __traits for templates, a lot of it is hacks and kludges.

It is used like


struct enumA
{
        int value;
        alias value this;
        @("float") enum Float = cast(enumA)0;
        @("int") enum Int = cast(enumA)1;
}

struct enumB
{
        int value;
        alias value this;
        @("double") enum Double = cast(enumB)0;
        @("byte") enum Byte = cast(enumB)1;
}

auto foo(T1, T2)(T1 a, T2 b)
{
        import std.conv;
        return to!string(a)~" - "~to!string(b);

}

void main()
{
    auto res = ()
    {
        int a = 4;
        double b = 1.23;
            enumA enumAVal = enumA.Float;
            enumB enumBVal = enumB.Byte;
mixin(EnumMapper!("foo", ["a", "b"], enumA, enumB, "enumAVal", "enumBVal")());
                return "--------";
    }();

        writeln(res);
        getchar();
}



and basically generates the nested switch structure:

---------------------
with(enumA) with(enumB)
switch(enumAVal)
{
        default: break;
        case Float:
                switch(enumBVal)
                {
                        default: break;
                        case Double:
return foo!(float, double)(cast(Parameters!(foo!(float, double))[0])a, cast(Parameters!(foo!(float, double))[1])b);
                                break;
                        case Byte:
return foo!(float, byte)(cast(Parameters!(foo!(float, byte))[0])a, cast(Parameters!(foo!(float, byte))[1])b);
                                break;
                }
                break;
        case Int:
                switch(enumBVal)
                {
                        default: break;
                        case Double:
return foo!(int, double)(cast(Parameters!(foo!(int, double))[0])a, cast(Parameters!(foo!(int, double))[1])b);
                                break;
                        case Byte:
return foo!(int, byte)(cast(Parameters!(foo!(int, byte))[0])a, cast(Parameters!(foo!(int, byte))[1])b);
                                break;
                }
                break;
}
---------------------


and so it maps the arbitrary (a,b) to the correct foo. The idea is simple: Given a templated function, we want map the arbitrary values, assuming they can be properly cast to the templated function depending on the enum values.

the enum values control which foo is called. But this works at runtime!

This is useful when one has many different representations of data that all can be overloaded, but one doesn't know which overload to use at compile time.

Could be used with variants to create automatic variant handlers also.

This is only useful when one templated function can handle all the cases though, just like how templates are used in the first place for overloading.

Maybe someone can clean it up and make it in to something special.






Reply via email to