www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template help

reply "Anonymous" <anon anon.com> writes:
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
parent "Anonymous" <anon anon.com> writes:
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