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