Last version for now, it can be improved further in various ways:
import std.typetuple: TypeTuple; import std.metastrings: Format; template Iota(int stop) { static if (stop <= 0) alias TypeTuple!() Iota; else alias TypeTuple!(Iota!(stop-1), stop-1) Iota; } enum string[] str_types = ["int", "float", "short"]; mixin(Format!("alias TypeTuple!(%s, %s, %s) types;", str_types[0], str_types[1], str_types[2])); static assert(str_types.length == types.length); // safety enum string[] str_sizes = ["100", "200", "250"]; mixin(Format!("enum int[] sizes = [%s, %s, %s];", str_sizes[0], str_sizes[1], str_sizes[2])); static assert(str_sizes.length == sizes.length); // safety void staticDispatch3(alias templ)(string run_t, string run_n, string run_m) { foreach (t; Iota!(str_types.length)) foreach (n; Iota!(str_sizes.length)) foreach (m; Iota!(str_sizes.length)) if (run_t == str_types[t] && run_n == str_sizes[n] && run_m == str_sizes[m]) { templ!(types[t], sizes[n], sizes[m])(); goto END; } assert(0); // safety END: {} } // ---------------------------- import std.stdio: writeln; void foo(T, int N, int M)() { writeln(typeid(T), " ", N, " ", M); } void main() { staticDispatch3!(foo)("int", "200", "100"); } A more generic staticDispatch3 like this doesn't work because items1 is a tuple, so it gets flattened... void staticDispatch3(alias func, alias str_items1, alias items1, alias str_items2, alias items2, alias str_items3, alias items3) (string run_i1, string run_i2, string run_i3) { static assert(str_items1.length == items1.length); static assert(str_items2.length == items2.length); static assert(str_items3.length == items3.length); foreach (i1; Iota!(items1.length)) foreach (i2; Iota!(items2.length)) foreach (i3; Iota!(items3.length)) if (run_i1 == str_types[i1] && run_i2 == str_types[i2] && run_i3 == str_types[i3]) { func!(items1[i1], items2[i2], items3[i3])(); goto END; } assert(0); // safety END: {} } Bye, bearophile