digitalmars.D.learn - replace switch for mapping
- EntangledQuanta (68/68) Aug 31 2017 Generally one has to use a switch to map dynamic components.
- EntangledQuanta (152/152) Sep 01 2017 I came up with a library solution that isn't pretty ;/
- Andrea Fontana (5/8) Sep 04 2017 Does this work for you?
- EntangledQuanta (5/14) Sep 04 2017 No, do you realize you are passing those enums at compile time?
- Andrea Fontana (5/22) Sep 05 2017 So at runtime you can do in a couple of ways if i'm right:
Generally one has to use a switch to map dynamic components. Given a set X and Y one can form a switch to map X to Y: switch(X) { case x1 : y1; break; .... case x1 : y1; } Is there any easier way to do this where one simply specifies the set's rather than having to create a switch directly? In my specific case, I have to map a two sets of types A = {Ta1,...,Tan} B = {Tb1,...,Tbm} to a template function that takes two types foo(Tak, Taj) so, given an arbitrary (a,b) in AxB, it it maps to foo(F(a),G(b)). Using switches would require n*m cases. What I actually have is something like enum X { Float, Int, `Etc` } and X x, y; and need to call foo!(x,y) but with x and y replaced by their correct D equivalent types. e.g., if x = X.Float; y = X.Int; then I need to call foo!(float,int) rather than foo!(X.Float,x.Int). This allows me to create a dynamic dispatcher at runtime and use a templated function rather than having to handle each type independently. One templated function rather than nxm regular functions for each type or a nxm switch. Unfortunately, a complicating factor is that the enum's names do not directly correspond to the D types through some simple transformation(e.g., lowerCase). D doesn't seem to support attributes on enum members for some inane reason and using strings will complicate things[https://forum.dlang.org/post/nmgloo$bd1$1 digitalmars.com]. I think I could use a struct though to solve that. So, given something like struct A { ("float") enum Float = 0, ("int") enum Int = 1, } struct B { ("double") enum Double = 0, ("short") enum Short = 1, } foo(T1,T2)(); create a mapping that takes an A and B and maps AxB to foo that does something like the following internally. fooDispatch(A a, B b) { switch(a) // Actually needs to be over attributes { case "float" : switch(b) // Actually needs to be over attributes { case "double" : return foo!(float, double)(); } ... } } or whatever. I could write a string mixin that generates the above code but I'm hoping I don't have to and some genius will find a simple way to do it quickly, efficiently, and performant.
Aug 31 2017
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.
Sep 01 2017
On Thursday, 31 August 2017 at 23:17:52 UTC, EntangledQuanta wrote:Generally one has to use a switch to map dynamic components. Given a set X and Y one can form a switch to map X to Y: [...]Does this work for you? https://dpaste.dzfl.pl/e2669b595539 Andrea
Sep 04 2017
On Monday, 4 September 2017 at 09:23:24 UTC, Andrea Fontana wrote:On Thursday, 31 August 2017 at 23:17:52 UTC, EntangledQuanta wrote:No, do you realize you are passing those enums at compile time? It won't work if they are "runtime" variables, which is the whole point of doing all this. You've essentially make a simple problem complicated. Why not just overload foo properly?Generally one has to use a switch to map dynamic components. Given a set X and Y one can form a switch to map X to Y: [...]Does this work for you? https://dpaste.dzfl.pl/e2669b595539 Andrea
Sep 04 2017
On Monday, 4 September 2017 at 20:54:27 UTC, EntangledQuanta wrote:On Monday, 4 September 2017 at 09:23:24 UTC, Andrea Fontana wrote:So at runtime you can do in a couple of ways if i'm right: http://dpaste.com/02W9FX6 AndreaOn Thursday, 31 August 2017 at 23:17:52 UTC, EntangledQuanta wrote:No, do you realize you are passing those enums at compile time? It won't work if they are "runtime" variables, which is the whole point of doing all this. You've essentially make a simple problem complicated. Why not just overload foo properly?Generally one has to use a switch to map dynamic components. Given a set X and Y one can form a switch to map X to Y: [...]Does this work for you? https://dpaste.dzfl.pl/e2669b595539 Andrea
Sep 05 2017