1 module dynamic;
2 
3 enum SymbolSet { all, skipDeprecated }
4 
5 mixin template dynamicBinding(alias mod, SymbolSet symbols = SymbolSet.all)
6 {
7 	import core.runtime : Runtime;
8 	import std.traits : ReturnType, ParameterTypeTuple, functionLinkage;
9 	import std.format : format;
10 
11 	alias _prototypes = prototypes!(mod, symbols);
12 
13 	private static string mixins()
14 	{
15 		string ret;
16 		foreach (i, proto; _prototypes) {
17 			enum varsuffix = __traits(getFunctionVariadicStyle, proto) == "none"
18 				? "" : ", ...";
19 
20 			ret ~= q{
21 				extern(%2$s) alias P_%1$s = ReturnType!(_prototypes[%3$d]) function(ParameterTypeTuple!(_prototypes[%3$d])) %5$-(%s %);
22 				P_%1$s p_%1$s;
23 				extern(%2$s) ReturnType!(_prototypes[%3$d]) %1$s(ParameterTypeTuple!(_prototypes[%3$d]) params%4$s)
24 				%5$-(%s %) {
25 					assert(p_%1$s !is null, "Function not loaded: %1$s");
26 					return p_%1$s(params);
27 				}
28 			}.format(__traits(identifier, proto), functionLinkage!proto, i,
29 				varsuffix, [__traits(getFunctionAttributes, proto)]);
30 		}
31 		return ret;
32 	}
33 	mixin(mixins());
34 
35 	void loadBinding(scope string[] library_files)
36 	{
37 		import std.conv : to;
38 		import std.format : format;
39 		import std.utf : toUTF16z;
40 		import std..string : toStringz;
41 		version (Windows) import core.sys.windows.windows : LoadLibraryW;
42 		else import core.sys.posix.dlfcn : dlopen, RTLD_LAZY;
43 
44 		foreach (f; library_files) {
45 			version (Windows) void* lib = LoadLibraryW(f.toUTF16z);
46 			else void* lib = dlopen(f.toStringz(), RTLD_LAZY);
47 			if (!lib) continue;
48 
49 			foreach (proto; _prototypes) {
50 				mixin(q{
51 					p_%1$s = cast(P_%1$s)loadProc(lib, proto.mangleof);
52 					if (!p_%1$s)
53 						throw new Exception(
54 							format("Failed to load function '%%s' from %1$s",
55 							proto.mangleof));
56 				}.format(__traits(identifier, proto)));
57 			}
58 			return;
59 		}
60 
61 		throw new Exception(format("Failed to load any of the shared library candidates: %(%s, %)", library_files));
62 	}
63 }
64 
65 /// private
66 template prototypes(alias mod, SymbolSet symbols)
67 {
68 	import std.meta : AliasSeq, staticMap;
69 
70 	template Overloads(string name) {
71 		static if (symbols == SymbolSet.skipDeprecated && isDeprecated!(mod, name))
72 			alias Overloads = AliasSeq!();
73 		else
74 			alias Overloads = AliasSeq!(__traits(getOverloads, mod, name));
75 	}
76 	alias functions = staticMap!(Overloads, AliasSeq!(__traits(allMembers, mod)));
77 
78 	/*template impl(size_t idx) {
79 		static if (idx < members.length) {
80 			alias impl = AliasSeq!(members[i], impl
81 		} else alias impl = AliasSeq!();
82 	}*/
83 	alias prototypes = functions;
84 }
85 
86 // crude workaround to gag deprecation warnings
87 private enum isDeprecated(alias parent, string symbol) =
88 	!__traits(compiles, {
89 		static assert(!__traits(isDeprecated, __traits(getMember, parent, symbol)));
90 	});
91 
92 /// private
93 void* loadProc(void* lib, string name)
94 {
95 	import std..string : toStringz;
96 
97 	version (Windows) {
98 		import core.sys.windows.windows;
99 		return GetProcAddress(lib, name.toStringz());
100 	} else {
101 		import core.sys.posix.dlfcn : dlsym;
102 		return dlsym(lib, name.toStringz());
103 	}
104 }