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