1 /// 2 module ssll; 3 4 import std.string : toStringz; 5 import std.exception : enforce; 6 7 version (Posix) 8 { 9 import core.sys.posix.dlfcn; 10 } 11 else version (Windows) 12 { 13 import core.sys.windows.winbase; 14 import core.sys.windows.windef : HINSTANCE; 15 } 16 else static assert(0, "unknown platform"); 17 18 struct ApiUDA { string libname; } 19 auto api(string lname="lib") { return ApiUDA(lname); } 20 21 string apiFunctionPointerName(string f) { return "__"~f~"_dlg"; } 22 23 version (Posix) alias LibHandler = void*; 24 version (Windows) alias LibHandler = HINSTANCE; 25 26 /// 27 LibHandler loadLibrary(string name) 28 { 29 if (name.length == 0) return null; 30 31 version (Posix) 32 return dlopen(name.toStringz, RTLD_LAZY); 33 version (Windows) 34 { 35 import core.stdc.stdlib : free, malloc; 36 37 import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar; 38 import core.sys.windows.winnt : WCHAR; 39 40 auto len = MultiByteToWideChar(CP_UTF8, 0, name.ptr, 41 cast(int)name.length, null, 0); 42 43 if (len == 0) return null; 44 45 auto buf = cast(WCHAR*)malloc((len+1) * WCHAR.sizeof); 46 if (buf is null) return null; 47 scope (exit) free(buf); 48 49 len = MultiByteToWideChar(CP_UTF8, 0, name.ptr, 50 cast(int)name.length, buf, len); 51 if (len == 0) return null; 52 53 buf[len] = '\0'; 54 return LoadLibraryW(buf); 55 } 56 } 57 58 /// 59 void unloadLibrary(ref LibHandler lib) 60 { 61 version (Posix) 62 { 63 dlclose(&lib); 64 lib = null; 65 } 66 version (Windows) 67 { 68 FreeLibrary(lib); 69 lib = null; 70 } 71 } 72 73 auto getSymbol(LibHandler lib, string name) 74 { 75 version (Posix) 76 return dlsym(lib, name.toStringz); 77 version (Windows) 78 return GetProcAddress(lib, name.toStringz); 79 } 80 81 private enum __initDeclare = q{ 82 import std.meta; 83 import std.typecons; 84 import std.traits; 85 import std.string; 86 //import core.sys.posix.dlfcn : dlsym; 87 88 enum __dimmy; 89 alias __this = AliasSeq!(__traits(parent, __dimmy))[0]; 90 enum __name = __traits(identifier, __this); 91 }; 92 93 private enum __callDeclare = q{ 94 enum __pit = [ParameterIdentifierTuple!__this]; 95 static if (!__pit.length) enum __params = ""; 96 else enum __params = "%-(%s, %)".format(__pit); 97 enum __call = "__fnc(%s);".format(__params); 98 static if (is(ReturnType!__this == void)) 99 enum __result = __call; 100 else 101 enum __result = "return " ~ __call; 102 mixin(__result); 103 }; 104 105 string rtLib() 106 { 107 return __initDeclare ~ q{ 108 mixin("auto __fnc = %s;".format(apiFunctionPointerName(__name))); 109 } ~ __callDeclare; 110 } 111 112 enum LoadApiSymbolsVerbose 113 { 114 none, 115 message, 116 assertion 117 } 118 119 mixin template apiSymbols() 120 { 121 import std.meta; 122 import std.typecons; 123 import std.traits; 124 125 enum __dimmy; 126 127 template funcsByUDA(alias symbol, uda) 128 { 129 template impl(lst...) 130 { 131 static if (lst.length == 1) 132 { 133 static if (is(typeof(__traits(getMember, symbol, lst[0])) == function)) 134 { 135 alias ff = AliasSeq!(__traits(getMember, symbol, lst[0]))[0]; 136 static if (hasUDA!(ff, uda)) alias impl = AliasSeq!(ff); 137 else alias impl = AliasSeq!(); 138 } 139 else alias impl = AliasSeq!(); 140 } 141 else alias impl = AliasSeq!(impl!(lst[0..$/2]), impl!(lst[$/2..$])); 142 } 143 144 alias funcsByUDA = impl!(__traits(allMembers, symbol)); 145 } 146 147 alias apiFuncs = funcsByUDA!(__traits(parent, __dimmy), ApiUDA); 148 149 void loadApiSymbols(LoadApiSymbolsVerbose verbose=LoadApiSymbolsVerbose.none) 150 { 151 import std.format : format; 152 import std.stdio : stderr; 153 154 foreach (f; apiFuncs) 155 { 156 enum libname = getUDAs!(f, ApiUDA)[$-1].libname; 157 enum fname = __traits(identifier, f); 158 enum pname = apiFunctionPointerName(fname); 159 mixin(format!`%2$s = cast(typeof(%2$s))getSymbol(%3$s, "%1$s");`(fname, pname, libname)); 160 if (mixin(pname ~ " is null")) 161 { 162 auto errmsg = format!`can't find '%s' function`(fname); 163 with (LoadApiSymbolsVerbose) final switch (verbose) 164 { 165 case none: break; 166 case message: stderr.writeln(errmsg); break; 167 case assertion: assert(0, errmsg); 168 } 169 } 170 } 171 } 172 173 mixin funcPointers!apiFuncs; 174 } 175 176 mixin template funcPointers(funcs...) 177 { 178 import std.string; 179 static if (funcs.length == 0) {} 180 else static if (funcs.length == 1) 181 { 182 alias __this = funcs[0]; 183 mixin(`private __gshared extern(C) @nogc nothrow ReturnType!__this function(Parameters!__this) %s;` 184 .format(apiFunctionPointerName(__traits(identifier, __this)))); 185 } 186 else 187 { 188 mixin funcPointers!(funcs[0..$/2]); 189 mixin funcPointers!(funcs[$/2..$]); 190 } 191 }