Mercurial > code
annotate modules/dynlib/dynlib.hpp @ 566:72390061044d
Net: improve documentation a bit
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 24 Jun 2016 13:00:35 +0200 |
parents | 5725e2d2ab4c |
children | 943d07fbe352 |
rev | line source |
---|---|
518 | 1 /* |
2 * dynlib.hpp -- portable shared library loader | |
3 * | |
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #ifndef DYNLIB_HPP | |
20 #define DYNLIB_HPP | |
21 | |
22 /** | |
23 * \file dynlib.hpp | |
24 * \brief Portable shared library loader. | |
25 * \author David Demelier <markand@malikania.fr> | |
26 */ | |
27 | |
28 /** | |
29 * \page Dynlib Dynlib | |
30 * \brief Portable shared library loader. | |
31 * | |
32 * The dynlib module let you open shared libraries dynamically at runtime. | |
33 * | |
34 * ## Operating system support | |
35 * | |
36 * | System | Support | Remarks | | |
37 * |---------|---------|--------------------| | |
38 * | Apple | Ok | | | |
39 * | FreeBSD | Ok | | | |
40 * | Linux | Ok | Needs -ldl library | | |
41 * | Windows | Ok | | | |
42 * | |
43 * ## How to export symbols | |
44 * | |
45 * When you want to dynamically load symbols from your shared library, make sure they are in a `extern "C"` block, if | |
46 * not they will be [mangled][name-mangling]. | |
47 * | |
48 * Note, this does not mean that you can't write C++ code, it just mean that you can't use namespaces and function | |
49 * overloading. | |
50 * | |
51 * Example of **plugin.cpp**: | |
52 * | |
53 * ````cpp | |
54 * #include <iostream> | |
55 * | |
56 * #include "dynlib.hpp" | |
57 * | |
58 * extern "C" { | |
59 * | |
60 * DYNLIB_EXPORT void plugin_load() | |
61 * { | |
62 * std::cout << "Loading plugin" << std::endl; | |
63 * } | |
64 * | |
65 * DYNLIB_EXPORT void plugin_unload() | |
66 * { | |
67 * std::cout << "Unloading plugin" << std::endl; | |
68 * } | |
69 * | |
70 * } | |
71 * ```` | |
72 * | |
73 * The \ref DYNLIB_EXPORT macro is necessary on some platforms to be sure that symbol will be visible. Make sure you always | |
74 * add it before any function. | |
75 * | |
76 * To compile, see your compiler documentation or build system. For gcc you can use the following: | |
77 * | |
78 * ```` | |
79 * gcc -std=c++14 -shared plugin.cpp -o plugin.so | |
80 * ```` | |
81 * | |
82 * ## How to load the library | |
83 * | |
84 * The dynlib module will search for the library in various places, thus you can use relative paths names but be sure | |
85 * that the library can be found. Otherwise, just use an absolute path to the file. | |
86 * | |
87 * ````cpp | |
88 * #include <iostream> | |
89 * | |
90 * #include "dynlib.hpp" | |
91 * | |
92 * int main() | |
93 * { | |
94 * try { | |
95 * Dynlib dso("./plugin" DYNLIB_SUFFIX); | |
96 * } catch (const std::exception &ex) { | |
97 * std::cerr << ex.what() << std::endl; | |
98 * } | |
99 * | |
100 * return 0; | |
101 * } | |
102 * ```` | |
103 * | |
104 * ## How to load symbol | |
105 * | |
106 * The last part is symbol loading, you muse use raw C function pointer and the Dynlib::sym function. | |
107 * | |
108 * ````cpp | |
109 * #include <iostream> | |
110 * | |
111 * #include "dynlib.hpp" | |
112 * | |
113 * using PluginLoad = void (*)(); | |
114 * using PluginUnload = void (*)(); | |
115 * | |
116 * int main() | |
117 * { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
118 * try { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
119 * Dynlib dso("./plugin" DYNLIB_SUFFIX); |
518 | 120 * |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
121 * dso.require<PluginLoad>("plugin_load")(); |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
122 * dso.require<PluginUnload>("plugin_unload")(); |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
123 * } catch (const std::exception &ex) { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
124 * std::cerr << ex.what() << std::endl; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
125 * } |
518 | 126 * |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
127 * return 0; |
518 | 128 * } |
129 * ```` | |
130 * | |
131 * [name-mangling]: https://en.wikipedia.org/wiki/Name_mangling | |
132 */ | |
133 | |
134 #include <stdexcept> | |
135 #include <string> | |
136 | |
137 #if defined(_WIN32) | |
138 # include <Windows.h> | |
139 #else | |
140 # include <dlfcn.h> | |
141 #endif | |
142 | |
143 /** | |
144 * \brief Export the symbol. | |
145 * | |
146 * This is required on some platforms and you should put it before your function signature. | |
147 * | |
148 * \code{.cpp} | |
149 * extern "C" { | |
150 * | |
151 * DYNLIB_EXPORT void my_function() | |
152 * { | |
153 * } | |
154 * | |
155 * } | |
156 * \endcode | |
157 */ | |
158 #if defined(_WIN32) | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
159 # define DYNLIB_EXPORT __declspec(dllexport) |
518 | 160 #else |
161 # define DYNLIB_EXPORT | |
162 #endif | |
163 | |
164 /** | |
165 * \brief Usual suffix for the library. | |
166 * | |
167 * This macro expands to the suffix convention for this platform. | |
168 * | |
169 * \code{.cpp} | |
170 * Dynlib library("./myplugin" DYNLIB_SUFFIX); | |
171 * \endcode | |
172 * | |
173 * \note Don't use the suffix expanded value shown in Doxygen as it may be wrong. | |
174 */ | |
175 #if defined(_WIN32) | |
176 # define DYNLIB_SUFFIX ".dll" | |
177 #elif defined(__APPLE__) | |
178 # define DYNLIB_SUFFIX ".dylib" | |
179 #else | |
180 # define DYNLIB_SUFFIX ".so" | |
181 #endif | |
182 | |
183 /** | |
184 * \class Dynlib | |
185 * \brief Load a dynamic module. | |
186 * | |
187 * This class is a portable wrapper to load shared libraries on supported systems. | |
188 */ | |
189 class Dynlib { | |
190 private: | |
191 #if defined(_WIN32) | |
557 | 192 using Handle = HMODULE; |
193 using Sym = FARPROC; | |
518 | 194 #else |
557 | 195 using Handle = void *; |
196 using Sym = void *; | |
518 | 197 #endif |
198 | |
199 public: | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
200 /** |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
201 * \brief Policy for symbol resolution. |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
202 */ |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
203 enum Policy { |
557 | 204 Immediately, //!< load symbols immediately |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
205 Lazy //!< load symbols when needed |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
206 }; |
518 | 207 |
208 private: | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
209 Handle m_handle; |
518 | 210 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
211 Dynlib(const Dynlib &) = delete; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
212 Dynlib &operator=(const Dynlib &) = delete; |
518 | 213 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
214 Dynlib(Dynlib &&) = delete; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
215 Dynlib &operator=(Dynlib &&) = delete; |
518 | 216 |
217 #if defined(_WIN32) | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
218 std::string error() |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
219 { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
220 LPSTR error = nullptr; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
221 std::string errmsg; |
518 | 222 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
223 FormatMessageA( |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
224 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
225 NULL, |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
226 GetLastError(), |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
227 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
228 (LPSTR)&error, 0, NULL); |
518 | 229 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
230 if (error) { |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
231 errmsg = std::string(error); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
232 LocalFree(error); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
233 } |
518 | 234 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
235 return errmsg; |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
236 } |
518 | 237 #endif |
238 | |
239 public: | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
240 /** |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
241 * Constructor to load a shared module. |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
242 * |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
243 * \param path the absolute path |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
244 * \param policy the policy to load |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
245 * \throw std::runtime_error on error |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
246 */ |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
247 Dynlib(const std::string &path, Policy policy = Immediately); |
518 | 248 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
249 /** |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
250 * Close the library automatically. |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
251 */ |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
252 ~Dynlib(); |
518 | 253 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
254 /** |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
255 * Get a symbol from the library. |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
256 * |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
257 * \param name the symbol |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
258 * \return the symbol |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
259 * \throw std::out_of_range on error |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
260 * \see DYNLIB_EXPORT |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
261 */ |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
262 template <typename T> |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
263 inline T require(const std::string &name) |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
264 { |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
265 auto sym = get<T>(name); |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
266 |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
267 if (!sym) |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
268 throw std::out_of_range(name + ": symbol not found"); |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
269 |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
270 return sym; |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
271 } |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
272 |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
273 /** |
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
274 * Get a symbol from the library or return nullptr if not found. |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
275 * |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
276 * \param name the symbol |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
277 * \return the symbol |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
278 */ |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
279 template <typename T> |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
280 T get(const std::string &name) noexcept; |
518 | 281 }; |
282 | |
283 #if defined(_WIN32) | |
284 | |
285 /* | |
286 * Windows implementation | |
287 * ------------------------------------------------------------------ | |
288 */ | |
289 | |
290 inline Dynlib::Dynlib(const std::string &path, Policy) | |
291 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
292 m_handle = LoadLibraryA(path.c_str()); |
518 | 293 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
294 if (m_handle == nullptr) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
295 throw std::runtime_error(error()); |
518 | 296 } |
297 | |
298 inline Dynlib::~Dynlib() | |
299 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
300 FreeLibrary(m_handle); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
301 m_handle = nullptr; |
518 | 302 } |
303 | |
304 template <typename T> | |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
305 inline T Dynlib::get(const std::string &name) noexcept |
518 | 306 { |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
307 return reinterpret_cast<T>(GetProcAddress(m_handle, name.c_str())); |
518 | 308 } |
309 | |
310 #else | |
311 | |
312 /* | |
313 * Unix implementation | |
314 * ------------------------------------------------------------------ | |
315 */ | |
316 | |
317 inline Dynlib::Dynlib(const std::string &path, Policy policy) | |
318 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
319 m_handle = dlopen(path.c_str(), policy == Immediately ? RTLD_NOW : RTLD_LAZY); |
518 | 320 |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
321 if (m_handle == nullptr) |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
322 throw std::runtime_error(dlerror()); |
518 | 323 } |
324 | |
325 inline Dynlib::~Dynlib() | |
326 { | |
548
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
327 dlclose(m_handle); |
f48bb09bccc7
Misc: huge cleanup, switch to spaces
David Demelier <markand@malikania.fr>
parents:
518
diff
changeset
|
328 m_handle = nullptr; |
518 | 329 } |
330 | |
331 template <typename T> | |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
332 inline T Dynlib::get(const std::string &name) noexcept |
518 | 333 { |
558
5725e2d2ab4c
Dynlib: add overload that does not throw
David Demelier <markand@malikania.fr>
parents:
557
diff
changeset
|
334 return reinterpret_cast<T>(dlsym(m_handle, name.c_str())); |
518 | 335 } |
336 | |
337 #endif | |
338 | |
339 #endif // !DYNLIB_HPP |