Map type to class instance at compile-time

pontius via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Oct 26 10:48:15 PDT 2016


Apologies for the long post or stupid questions, I only started 
to learn D today.
I have a use case where various types (A, B, C) need to be 
associated with instances of different classes (ManagerA, 
ManagerB, ManagerC). A certain object (globalManager) should 
redirect operations on those types to their respective managers 
at compile-time. I intend to use it in the following way:

class GlobalManager(TypeToManagerMapping) {
     public void process(T)(T t) {
         // Must be resolved at compile-time
         TypeToManagerMapping.getManagerForType(T).process(t);
     }
}

// All these types can be completely unrelated, i.e. no base 
classes
struct A {}
alias ManagerA = DefaultManager!(A)
struct B {}
alias ManagerB = DefaultManager!(B)
struct C {}
alias ManagerC = SomeCustomManager;

// Calls to globalManager should be redirected to these at 
compile-time
auto mgrA = new ManagerA();
auto mgrB = new ManagerB();
auto mgrC = new ManagerC();

// This is my problem, see below
alias TypeToManagerMapping = ...;

// Pass the mapping as a template parameter so that it can be 
resolved at compile-time
auto globalManager = new GlobalManager!(TypeToManagerMapping)();

// The following is the module user's code.
// The user may not be aware of mgrA, mgrB, etc, only of 
globalManager

A a = A();
B b = B();
C c = C();

// Redirection to managers in the following operations
// must be resolved at compile-time

// Should turn into mgrA.process(a), etc
globalManager.process(a);
globalManager.process(b);
globalManager.process(c);

So, I need to map a type (e.g. A) to an instance (e.g. mgrA). I 
have managed to implement a compile-time map from type to value 
(by the way, how do I get rid of TValue argument in 
TypeValuePair? it can be deduced, but I failed to get it working 
with an eponymous template):

struct TypeValuePair(TKey_, TValue, TValue value_) {
	alias TKey = TKey_;
	static const TValue value = value_;
}

struct StaticMap(THead, Args...) {
	template get(T) {
		static if (Args.length < 0) {
			static assert(false, "StaticMap does not contain this key");
		} else static if (is(T == THead.TKey)) {
			alias get = THead.value;
		} else {
			alias get = StaticMap!(Args).get!(T);
		}
	}
}

This works nicely for mapping types to literal values:

alias TypeMap = StaticMap!(
	TypeValuePair!(string, string, "a string"),
	TypeValuePair!(int, string, "an int"),
	TypeValuePair!(bool, string, "a bool")
);
writeln(TypeMap.get!(int));

But fails with "variable cannot be read at compile-time" when I 
try to pass a class instance in there:

alias TypeMap = StaticMap!(
	TypeValuePair!(A, ManagerA, mgrA)
);
TypeMap.get!(A).process(a);

So, how do I resolve type-to-instance mapping at compile-time so 
that my user only needs to call globalManager and not know 
anything about individual managers?

I could easily do this with typeid() calls, but the solution must 
be purely compile-time (for learning purposes; let's say my code 
is performance-critical and the lookup would take considerable 
amount of time).


More information about the Digitalmars-d-learn mailing list