1 
2 module ws.sys.library;
3 
4 import
5 	std.string,
6 	std.conv,
7 	std.traits,
8 	ws.log;
9 
10 version(Windows) import derelict.util.wintypes;
11 version(Posix) import core.sys.posix.dlfcn;
12 
13 
14 string embedFunc(string n, string t)(){
15 	return t ~ ' ' ~ n ~ ";\n";
16 }
17 
18 string embedFunc(string n, T)(){
19 	return fullyQualifiedName!T ~ ' ' ~ n ~ ";\n";
20 }
21 
22 string getFunc()(){
23 	return "";
24 }
25 
26 string getFunc(string n, T, More...)(){
27 	return embedFunc!(n, T)() ~ getFunc!More();
28 	//return t ~ ' ' ~ n ~ ";\n" ~ getFunc!More();
29 }
30 
31 string getFunc(string n, string t, More...)(){
32 	return embedFunc!(n, t)() ~ getFunc!More();
33 }
34 
35 string loadFunc(string f)(){
36 	return f ~ " = cast(typeof(" ~ f ~ "))l.get(\"" ~ f ~ "\");";
37 }
38 
39 
40 bool compiles(string c)(){
41 	mixin("return __traits(compiles, " ~ c ~ ");");
42 }
43 
44 
45 Library fillLibrary(alias Fillable)(string path){
46 	auto library = new Library(path);
47 	foreach(member; __traits(allMembers, Fillable)){
48 		mixin("enum compiles = __traits(compiles, Fillable."~member~" !is null);");
49 		static if(compiles){
50 			mixin("enum isFP = isFunctionPointer!(Fillable." ~ member ~ ");");
51 			static if(isFP){
52 				mixin("assert(!hasUnsharedAliasing!(typeof(Fillable." ~ member ~ ")));");
53 				mixin("Fillable." ~ member ~ " = cast(typeof(Fillable." ~ member ~ "))library.get(member);");
54 				mixin("assert(Fillable." ~ member ~ " != null);");
55 			}
56 		}
57 	}
58 	return library;
59 }
60 
61 
62 mixin template library(string refname, string name, Args...){
63 
64 	mixin("
65 		struct " ~ refname ~ " {
66 			import ws.log;
67 			__gshared Library lib;
68 			shared static this(){
69 				try
70 					lib = load();
71 				catch(Exception e){
72 					ws.log.Log.error(e.toString());
73 					throw e;
74 				}
75 			}
76 			static Library load(){
77 				Library l = new Library(\"" ~ name ~ "\");
78 				loadFuncs!Args(l);
79 				return l;
80 			}
81 		}
82 	");
83 	
84 	void loadFuncs(string f, More...)(Library l){
85 		mixin(loadFunc!(f)());
86 		static if(More.length >= 2)
87 			loadFuncs!(More[1..$])(l);
88 	}
89 
90 	// put last due to possible override
91 	mixin(getFunc!Args());
92 
93 }
94 
95 class Library {
96 
97 	void* get(string s){
98 		void* p = null;
99 		version(Windows)
100 			p = GetProcAddress(lib, s.toStringz());
101 		version(Posix)
102 			p = dlsym(lib, s.toStringz);
103 		if(!p)
104 			throw new Exception("Failed to load symbol \"" ~ s ~ "\": " ~ getError());
105 		return p;
106 	}
107 	
108 
109 	T call(T, string name, Args...)(Args args){
110 		alias T function(Args) FT;
111 		static FT func;
112 		if(!func)
113 			func = cast(FT)get(name);
114 		return func(args);
115 	}
116 	
117 
118 	this(string s, string vers = ""){
119 		version(Windows)
120 			lib = LoadLibraryA(s.toStringz());
121 		version(Posix)
122 			lib = dlopen(("lib" ~ s ~ ".so" ~ vers).toStringz(), RTLD_NOW);
123 		if(!lib)
124 			throw new Exception("Failed to load library \"" ~ s ~ "\": " ~ getError());
125 	}
126 
127 
128 	~this(){
129 		version(Windows)
130 			FreeLibrary(lib);
131 		version(Posix)
132 			dlclose(lib);
133 	}
134 
135 	version(Windows) private HMODULE lib;
136 	version(Posix) private void* lib;
137 
138 	private string getError(){
139 		version(Windows){
140 			DWORD errcode = GetLastError();
141 			LPCSTR msgBuf;
142 			DWORD i = FormatMessageA(
143 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
144 				FORMAT_MESSAGE_FROM_SYSTEM |
145 				FORMAT_MESSAGE_IGNORE_INSERTS,
146 				null,
147 				errcode,
148 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
149 				cast(LPCSTR)&msgBuf,
150 				0,
151 				null);
152 			string text = to!string(msgBuf);
153 			LocalFree(cast(HLOCAL)msgBuf);
154 			if(i >= 2)
155 				i -= 2;
156 			return text[0 .. i] ~ " (%s)".format(errcode);
157 		}version(Posix){
158 			auto err = dlerror();
159 			if(!err)
160 				return "Unknown Error";
161 			return to!string(err);		
162 		}
163 	}
164 }
165 
166 
167 class AutoLibrary {
168 
169 	protected {
170 		Library lib;
171 		string prefix;
172 
173 	}
174 
175 	this(string name, string prefix){
176 		this.prefix = prefix;
177 		lib = new Library(name);
178 	}
179 
180 
181 
182 }
183