1 module dynamic;
2 
3 enum SymbolSet { all, skipDeprecated }
4 
5 
6 /** Declares a dynamically linked library binding.
7 
8 	Params:
9 		mod = Alias of a module that contains a static binding consisting of
10 			function prototypes
11 		symbols = Optionally disables binding of deprecated symbols
12 
13 	Example:
14 		---
15 		import dynamic;
16 
17 		import my_c_library : myCLibFunction;
18 
19 		// define the dynamic binding
20 		dynamicBinding!my_c_library myCLibBinding;
21 
22 		void main()
23 		{
24 			// load the actual symbols from the dynamic library
25 			version (Windows) myCLibBinding.loadBinding("myclib.dll");
26 			else version (OSX) myCLibBinding.loadBinding("libmyclib.dylib");
27 			else myCLibBinding.loadBinding("libmyclib.so", "libmyclib.so.1");
28 
29 			// now we can call a function from `my_c_library` as if it were a
30 			// simple statically linked function:
31 			myCLibFunction();
32 		}
33 		---
34 */
35 mixin template dynamicBinding(alias mod, SymbolSet symbols = SymbolSet.all)
36 {
37 	import core.runtime : Runtime;
38 	import std.array : join;
39 	import std.traits : ReturnType, ParameterTypeTuple, functionLinkage;
40 
41 	alias _prototypes = prototypes!(mod, symbols);
42 	private enum _id(alias p) = __traits(identifier, p);
43 
44 	static foreach (i, proto; _prototypes) {
45 		mixin("extern("~functionLinkage!proto~") alias P_"~_id!proto~" = ReturnType!proto function(ParameterTypeTuple!proto) " ~ join([__traits(getFunctionAttributes, proto)], " ") ~ ";");
46 		mixin("P_"~_id!proto~" p_"~_id!proto~";");
47 		mixin("extern("~functionLinkage!proto~") ReturnType!proto "~_id!proto~"(ParameterTypeTuple!proto params"~(__traits(getFunctionVariadicStyle, proto) == "none"
48 				? "" : ", ...")~") "~join([__traits(getFunctionAttributes, proto)], " ")~" {\n"
49 			~ "  assert(p_"~_id!proto~" !is null, \"Function not loaded: "~_id!proto~"\");\n"
50 			~ "  return p_"~_id!proto~"(params);\n"
51 			~ "}");
52 	}
53 
54 	void loadBinding(scope string[] library_files...)
55 	{
56 		import std.conv : to;
57 		import std.format : format;
58 		import std.utf : toUTF16z;
59 		import std.string : toStringz;
60 		version (Windows) import core.sys.windows.windows : LoadLibraryW;
61 		else import core.sys.posix.dlfcn : dlopen, RTLD_LAZY;
62 
63 		foreach (f; library_files) {
64 			version (Windows) void* lib = LoadLibraryW(f.toUTF16z);
65 			else void* lib = dlopen(f.toStringz(), RTLD_LAZY);
66 			if (!lib) continue;
67 
68 			foreach (proto; _prototypes) {
69 				enum ident = __traits(identifier, proto);
70 				mixin("p_"~ident) = cast(typeof(mixin("p_"~ident)))loadProc(lib, proto.mangleof);
71 				if (!mixin("p_"~ident))
72 					throw new Exception("Failed to load function '"~proto.mangleof~"' from " ~ f);
73 			}
74 			return;
75 		}
76 
77 		throw new Exception(format("Failed to load any of the shared library candidates: %(%s, %)", library_files));
78 	}
79 }
80 
81 /// private
82 template prototypes(alias mod, SymbolSet symbols)
83 {
84 	import std.meta : AliasSeq, staticMap;
85 
86 	template Overloads(string name) {
87 		static if (symbols == SymbolSet.skipDeprecated && isDeprecated!(mod, name))
88 			alias Overloads = AliasSeq!();
89 		else
90 			alias Overloads = AliasSeq!(__traits(getOverloads, mod, name));
91 	}
92 	alias functions = staticMap!(Overloads, AliasSeq!(__traits(allMembers, mod)));
93 
94 	/*template impl(size_t idx) {
95 		static if (idx < members.length) {
96 			alias impl = AliasSeq!(members[i], impl
97 		} else alias impl = AliasSeq!();
98 	}*/
99 	alias prototypes = functions;
100 }
101 
102 // crude workaround to gag deprecation warnings
103 private enum isDeprecated(alias parent, string symbol) =
104 	!__traits(compiles, {
105 		static assert(!__traits(isDeprecated, __traits(getMember, parent, symbol)));
106 	});
107 
108 /// private
109 void* loadProc(void* lib, string name)
110 {
111 	import std.string : toStringz;
112 
113 	version (Windows) {
114 		import core.sys.windows.windows;
115 		return GetProcAddress(lib, name.toStringz());
116 	} else {
117 		import core.sys.posix.dlfcn : dlsym;
118 		return dlsym(lib, name.toStringz());
119 	}
120 }