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 }