digitalmars.D.learn - Template help
I got to typing one day and came up with this. What it does is search an aggregate for a member named match. If it's a direct member, it evaluates to that. If it's not, then it searches any aggregate type sub-members (deep members) for match. If there's only one deep member tree with match, it evaluates to that. Otherwise you get static assertion errors for no match or an ambiguous match. Maybe I'm doing something completely wrong in my code, and I certainly don't think it's pretty, but it seems to work how I expect. So say some variable Foo has member Bar. Bar has member Baz, which has member Thing. Instead of writing auto x = Foo.Bar.Baz.Thing; you can write auto x = Foo.Extract!"Thing". The trouble comes in if there's more than one tree with Thing in it. Except when I want a deep member that happens to be a function. I can't for the life of me figure out how to recurse down the member hierarchy while carrying along the arguments, or bubble up some alias to the function at the bottom which I then pass the arguments. If I get past that, I want to try and play with automatic, ad-hoc type generation based on X functions you try to call on Y members. template agg_mems(T) { enum T_instance = T.init; alias pred(string name) = isAggregateType!(totype!name); alias totype(string name) = typeof(__traits(getMember,T_instance,name)); alias agg_mems = Filter!(pred, __traits(allMembers,T)); } /** Evaluates to true if match is a member or sub-member of T, and false otherwise. Issues assertion failure if more than one sub-member is a match. Used primarily by DeepMember. */ template hasDeepMember(T, string match) { static if (hasMember!(T,match)) enum hasDeepMember = true; else { enum T_instance = T.init; alias name_list = agg_mems!T; //flatten our hasDeepMember to one parameter, a member name alias test_unary(string name) = hasDeepMember!(typeof(__traits(getMember,T_instance,name)), match); alias haves = Filter!(test_unary, name_list); static assert(haves.length < 2,T.stringof~" ambiguous match for "~match); //if we find a match among sub-members, evals to true static if (haves.length == 1) enum hasDeepMember = true; else enum hasDeepMember = false; } } template DeepMember(T, string match, parent...) { //test match static if (hasMember!(T,match)) enum DeepMember = TypeTuple!(parent, match); else { enum T_instance = T.init; alias name_list = agg_mems!(T); //flatten our presence test down to one parameter for Filter alias test_unary(string name) = hasDeepMember!(typeof(__traits(getMember,T_instance,name)), match); alias next = Filter!(test_unary, name_list); static assert(next.length < 2,T.stringof~" has ambiguous match for "~match); static if (next.length == 1) { //accumulate hierarchy of members enum DeepMember = DeepMember!( typeof(__traits(getMember,T_instance,next[0])), match, TypeTuple!(parent,next[0])); } else static assert(false,T.stringof~" has no match for "~match); } } auto ref Extract(string match, T, S...)(auto ref T var, auto ref S args) { static if (hasMember!(T, match)) { static if (args.length == 0) return __traits(getMember,var,match); else return __traits(getMember,var,match)(args); } else { alias tree = DeepMember!(T, match); static assert(tree.length > 0); return DeepExtract!(T,tree)(var); } } auto ref DeepExtract(T, tree...)(auto ref T var) { static if (tree.length == 1) return __traits(getMember,var,tree[0]); else { alias next_T = typeof(__traits(getMember,var,tree[0])); return DeepExtract!(next_T, tree[1..$])(__traits(getMember,var,tree[0])); } }
Jul 12 2014
One way I've used it in code struct MapBy(T,string key) if (hasDeepMember!(T,key)) { alias key_t = DeepMemberType!(T,key); private const(T)[key_t] _map; bool has(key_t id) const nothrow { if ((id in _map) != null) return true; else return false; } void put(in ref T val) pure nothrow { auto id = val.Extract!key; _map[id] = val; } ref const(T) get(key_t id) const nothrow { assert(has(id)); return _map[id]; } }
Jul 12 2014