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     mixin((is(ReturnType!__self_function__ == void) ? "" : "return ") ~
78             apiFunctionPointerName!(__traits(identifier, __self_function__)) ~
79         "(" ~ commaSeparated!([ParameterIdentifierTuple!__self_function__]) ~ ");");
80 };
81 
82 enum LoadApiSymbolsVerbose
83 {
84     none,
85     message,
86     assertion
87 }
88 
89 mixin template SSLL_INIT()
90 {
91     alias apiFuncs = funcsByUDA!(__traits(parent, loadApiSymbols), ApiUDA);
92 
93     void loadApiSymbols(LoadApiSymbolsVerbose verbose=LoadApiSymbolsVerbose.none)
94     {
95         version (Posix)
96         {
97             import core.sys.posix.dlfcn : dlsym;
98             alias getSymbol = dlsym;
99         }
100         version (Windows)
101         {
102             import core.sys.windows.windows : GetProcAddress;
103             alias getSymbol = GetProcAddress;
104         }
105 
106         foreach (f; apiFuncs)
107         {
108             enum libname = getUDAs!(f, ApiUDA)[$-1].libname;
109             enum fname = __traits(identifier, f) ~ '\0';
110             enum pname = apiFunctionPointerName!(fname[0..$-1]);
111             mixin(pname ~ " = cast(typeof(" ~ pname ~ "))getSymbol("
112                                 ~ libname ~ ", fname.ptr);");
113             if (mixin(pname ~ " is null"))
114             {
115                 with (LoadApiSymbolsVerbose) final switch (verbose) 
116                 {
117                     case none: break;
118                     case message:
119                         import core.stdc.stdio : printf;
120                         printf("can't find '%s' function\n", fname.ptr);
121                         break;
122                     case assertion: assert(0, fname[0..$-1]);
123                 }
124             }
125         }
126     }
127 
128     mixin funcPointers!apiFuncs;
129 }
130 
131 template apiFunctionPointerName(string f)
132 { enum apiFunctionPointerName = "__" ~ f ~"_fnc_ptr"; }
133 
134 template funcsByUDA(alias symbol, uda)
135 {
136     template impl(lst...)
137     {
138         static if (lst.length == 1)
139         {
140             static if (is(typeof(__traits(getMember, symbol, lst[0])) == function))
141             {
142                 alias ff = AliasSeq!(__traits(getMember, symbol, lst[0]))[0];
143                 static if (hasUDA!(ff, uda)) alias impl = AliasSeq!(ff);
144                 else alias impl = AliasSeq!();
145             }
146             else alias impl = AliasSeq!();
147         }
148         else alias impl = AliasSeq!(impl!(lst[0..$/2]), impl!(lst[$/2..$]));
149     }
150 
151     alias funcsByUDA = impl!(__traits(allMembers, symbol));
152 }
153 
154 mixin template funcPointers(funcs...)
155 {
156     static if (funcs.length == 0) {}
157     else static if (funcs.length == 1)
158     {
159         alias __this = funcs[0];
160         mixin(`private __gshared extern(C) @nogc nothrow ReturnType!__this function(Parameters!__this) ` ~
161                 apiFunctionPointerName!(__traits(identifier, __this)) ~ `;`);
162     }
163     else
164     {
165         mixin funcPointers!(funcs[0..$/2]);
166         mixin funcPointers!(funcs[$/2..$]);
167     }
168 }