import std.variant; // this GeneralLibrary gives the base, this time with some methods we can actually use interface GeneralLibrary { @system auto opDispatch(string name, string file = __FILE__, size_t line = __LINE__, T...)(T t) { Variant[] args; foreach(arg; t) { args ~= Variant(arg); } return dynamicFunctionCall(name, args, file, line); } // file and line are for better error messages Variant dynamicFunctionCall(string name, Variant[] args, string file, size_t line); } // same as before, just gives us easier access to our versioned class names template Library(string libver) { import std.array; mixin("alias Library_" ~ std.array.replace(libver, ".", "_") ~ " Library;"); } // our errors class DynamicMethodCallException : Exception { this(string msg, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line); } } class ArgumentMismatchException : DynamicMethodCallException { this(string msg, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line); } } class NoSuchMethodException : DynamicMethodCallException { this(string method, string file = __FILE__, size_t line = __LINE__) { super("No such method: " ~ method, file, line); } } // this does the conversion from Variant[] into the original types of the function Variant dynamicCallHelper(func)(func callable, Variant[] args, string file = __FILE__, size_t line = __LINE__) { import std.traits; import std.string; ParameterTypeTuple!func typedArgs; foreach(i, ref arg; typedArgs) { if(i >= args.length) throw new ArgumentMismatchException("Not enough arguments to call " ~ func.stringof, file, line); auto var = args[i].peek!(typeof(arg)); if(var is null) throw new ArgumentMismatchException(format("Calling %s failed because argument %d was not type %s", func.stringof, i, typeof(arg).stringof), file, line); arg = *var; } Variant v; static if(is(ReturnType!func == void)) callable(typedArgs); else v = Variant(callable(typedArgs)); return v; } auto createLibrary(string libver, Ctor_Args...)(Ctor_Args ctor_args) { import std.traits; auto obj = new Library!(libver)(ctor_args); return new class GeneralLibrary { override Variant dynamicFunctionCall(string name, Variant[] args, string file, size_t line) { foreach(func; __traits(allMembers, typeof(obj))) { static if(__traits(compiles, dynamicCallHelper(&__traits(getMember, obj, func), args, file, line))) { static if(func != "factory") // HACK around "Error: delegates are only for non-static functions". It shouldn't have passed the above line i think... seems like a compiler bug, but whatever, let's just work around it and move on if(name == func) return dynamicCallHelper(&__traits(getMember, obj, func), args, file, line); } } // if we get here, the name wasn't found in the all members loop, which checks base classes too btw throw new NoSuchMethodException(name, file, line); } // this is still here to allow for the static example to work too @system auto opDispatch(string name, T...)(T t) { return mixin("obj." ~ name ~ "(t)"); } }; } void main() { GeneralLibrary library = createLibrary!("1.0"); GeneralLibrary library11 = createLibrary!("1.1"); // if you use auto library11 instead of GeneralLibrary, you'll still get static typing and checks GeneralLibrary library12 = createLibrary!("1.2"); GeneralLibrary library2 = createLibrary!("2.0"); /* library.func1(); // Should work library.func11(); // Should fail library.func12(); // Should fail library.func2(); // Should fail */ library11.func1(); // Should work library11.func11(); // Should work library11.func12(); // Should fail library11.func2(); // Should fail } class Library_1_0 { void func1() {} string funcstr() { return "test"; } } class Library_1_1 : Library_1_0 { void func11() {} } class Library_1_2 : Library_1_1 { void func12() {} } class Library_2_0 { void func2() {} }