1 ///
2 module ssll;
3 
4 import core.sys.posix.dlfcn;
5 
6 import std.string : toStringz;
7 import std.exception : enforce;
8 
9 struct ApiUDA { string libname; }
10 auto api(string lname="lib") { return ApiUDA(lname); }
11 
12 string apiFunctionPointerName(string f) { return "__"~f~"_dlg"; }
13 
14 ///
15 void* loadLibrary(string name)
16 {
17     return dlopen(name.toStringz, RTLD_LAZY);
18 }
19 
20 ///
21 void unloadLibrary(ref void* lib)
22 {
23     dlclose(&lib);
24     lib = null;
25 }
26 
27 private enum __initDeclare = q{
28     import std.meta;
29     import std.typecons;
30     import std.traits;
31     import std.string;
32     import core.sys.posix.dlfcn : dlsym;
33 
34     enum __dimmy;
35     alias __this = AliasSeq!(__traits(parent, __dimmy))[0];
36     enum __name = __traits(identifier, __this);
37 };
38 
39 private enum __callDeclare = q{
40     enum __pit = [ParameterIdentifierTuple!__this];
41     static if (!__pit.length) enum __params = "";
42     else enum __params = "%-(%s, %)".format(__pit);
43     enum __call = "__fnc(%s);".format(__params);
44     static if (is(ReturnType!__this == void))
45         enum __result = __call;
46     else
47         enum __result = "return " ~ __call;
48     mixin(__result);
49 };
50 
51 string rtLib()
52 {
53     return __initDeclare ~ q{
54     mixin("auto __fnc = %s;".format(apiFunctionPointerName(__name)));
55     } ~ __callDeclare;
56 }
57 
58 mixin template apiSymbols()
59 {
60     import std.meta;
61     import std.typecons;
62     import std.traits;
63 
64     enum __dimmy;
65 
66     template funcsByUDA(alias symbol, uda)
67     {
68         template impl(lst...)
69         {
70             static if (lst.length == 1)
71             {
72                 static if (is(typeof(__traits(getMember, symbol, lst[0])) == function))
73                 {
74                     alias ff = AliasSeq!(__traits(getMember, symbol, lst[0]))[0];
75                     static if (hasUDA!(ff, uda)) alias impl = AliasSeq!(ff);
76                     else alias impl = AliasSeq!();
77                 }
78                 else alias impl = AliasSeq!();
79             }
80             else alias impl = AliasSeq!(impl!(lst[0..$/2]), impl!(lst[$/2..$]));
81         }
82 
83         alias funcsByUDA = impl!(__traits(allMembers, symbol));
84     }
85 
86     alias apiFuncs = funcsByUDA!(__traits(parent, __dimmy), ApiUDA);
87 
88     void loadApiSymbols()
89     {
90         import std.string;
91         import core.sys.posix.dlfcn;
92         foreach (f; apiFuncs)
93         {
94             enum libname = getUDAs!(f, ApiUDA)[$-1].libname;
95             enum fname = __traits(identifier, f);
96             enum pname = apiFunctionPointerName(fname);
97             mixin(format(`%2$s = cast(typeof(%2$s))dlsym(%3$s, "%1$s".toStringz);`, fname, pname, libname));
98         }
99     }
100 
101     mixin funcPointers!apiFuncs;
102 }
103 
104 mixin template funcPointers(funcs...)
105 {
106     import std.string;
107     static if (funcs.length == 0) {}
108     else static if (funcs.length == 1)
109     {
110         alias __this = funcs[0];
111         mixin(`private extern(C) @nogc nothrow ReturnType!__this function(Parameters!__this) %s;`
112                 .format(apiFunctionPointerName(__traits(identifier, __this))));
113     }
114     else
115     {
116         mixin funcPointers!(funcs[0..$/2]);
117         mixin funcPointers!(funcs[$/2..$]);
118     }
119 }