]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Copyright 2014 Renato Tegon Forti, Antony Polukhin. |
2 | // Copyright 2015-2016 Antony Polukhin. | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. | |
5 | // (See accompanying file LICENSE_1_0.txt | |
6 | // or copy at http://www.boost.org/LICENSE_1_0.txt) | |
7 | ||
8 | #ifndef BOOST_DLL_SHARED_LIBRARY_HPP | |
9 | #define BOOST_DLL_SHARED_LIBRARY_HPP | |
10 | ||
11 | /// \file boost/dll/shared_library.hpp | |
12 | /// \brief Contains the boost::dll::shared_library class, core class for all the | |
13 | /// DLL/DSO operations. | |
14 | ||
15 | #include <boost/config.hpp> | |
16 | #include <boost/predef/os.h> | |
17 | #include <boost/utility/enable_if.hpp> | |
18 | #include <boost/type_traits/is_member_pointer.hpp> | |
19 | #include <boost/utility/explicit_operator_bool.hpp> | |
20 | #include <boost/dll/detail/system_error.hpp> | |
21 | #include <boost/dll/detail/aggressive_ptr_cast.hpp> | |
22 | ||
23 | #if BOOST_OS_WINDOWS | |
24 | # include <boost/dll/detail/windows/shared_library_impl.hpp> | |
25 | #else | |
26 | # include <boost/dll/detail/posix/shared_library_impl.hpp> | |
27 | #endif | |
28 | ||
29 | #ifdef BOOST_HAS_PRAGMA_ONCE | |
30 | # pragma once | |
31 | #endif | |
32 | ||
33 | namespace boost { namespace dll { | |
34 | ||
35 | /*! | |
36 | * \brief This class can be used to load a | |
37 | * Dynamic link libraries (DLL's) or Shared Libraries, also know | |
38 | * as dynamic shared objects (DSO's) and get their exported | |
39 | * symbols (functions and variables). | |
40 | * | |
41 | * shared_library instances share reference count to an actual loaded DLL/DSO, so it | |
42 | * is safe and memory efficient to have multiple instances of shared_library referencing the same DLL/DSO | |
43 | * even if those instances were loaded using different paths (relative + absolute) referencing the same object. | |
44 | * | |
45 | * On Linux/POSIX link with library "dl". "-fvisibility=hidden" flag is also recommended for use on Linux/POSIX. | |
46 | */ | |
47 | class shared_library | |
48 | /// @cond | |
49 | : private boost::dll::detail::shared_library_impl | |
50 | /// @endcond | |
51 | { | |
52 | typedef boost::dll::detail::shared_library_impl base_t; | |
53 | BOOST_COPYABLE_AND_MOVABLE(shared_library) | |
54 | ||
55 | public: | |
56 | #ifdef BOOST_DLL_DOXYGEN | |
57 | typedef platform_specific native_handle_t; | |
58 | #else | |
59 | typedef shared_library_impl::native_handle_t native_handle_t; | |
60 | #endif | |
61 | ||
62 | /*! | |
63 | * Creates in anstance that does not reference any DLL/DSO. | |
64 | * | |
65 | * \post this->is_loaded() returns false. | |
66 | * \throw Nothing. | |
67 | */ | |
68 | shared_library() BOOST_NOEXCEPT {} | |
69 | ||
70 | /*! | |
71 | * Copy constructor that increments the reference count of an underlying shared library. | |
72 | * Same as calling constructor with `lib.location()` parameter. | |
73 | * | |
74 | * \param lib A library to copy. | |
75 | * \post lib == *this | |
76 | * \throw boost::system::system_error, std::bad_alloc in case of insufficient memory. | |
77 | */ | |
78 | shared_library(const shared_library& lib) | |
79 | : base_t() | |
80 | { | |
81 | assign(lib); | |
82 | } | |
83 | ||
84 | /*! | |
85 | * Copy constructor that increments the reference count of an underlying shared library. | |
86 | * Same as calling constructor with `lib.location(), ec` parameters. | |
87 | * | |
88 | * \param lib A shared library to copy. | |
89 | * \param ec Variable that will be set to the result of the operation. | |
90 | * \post lib == *this | |
91 | * \throw std::bad_alloc in case of insufficient memory. | |
92 | */ | |
93 | shared_library(const shared_library& lib, boost::system::error_code& ec) | |
94 | : base_t() | |
95 | { | |
96 | assign(lib, ec); | |
97 | } | |
98 | ||
99 | /*! | |
100 | * Move constructor. Does not invalidate existing symbols and functions loaded from lib. | |
101 | * | |
102 | * \param lib A shared library to move from. | |
103 | * \post lib.is_loaded() returns false, this->is_loaded() return true. | |
104 | * \throw Nothing. | |
105 | */ | |
106 | shared_library(BOOST_RV_REF(shared_library) lib) BOOST_NOEXCEPT | |
107 | : base_t(boost::move(static_cast<base_t&>(lib))) | |
108 | {} | |
109 | ||
110 | /*! | |
111 | * Loads a library by specified path with a specified mode. | |
112 | * | |
113 | * \param lib_path Library file name. Can handle std::string, const char*, std::wstring, | |
114 | * const wchar_t* or boost::filesystem::path. | |
115 | * \param mode A mode that will be used on library load. | |
116 | * \throw boost::system::system_error, std::bad_alloc in case of insufficient memory. | |
117 | */ | |
118 | explicit shared_library(const boost::filesystem::path& lib_path, load_mode::type mode = load_mode::default_mode) { | |
119 | shared_library::load(lib_path, mode); | |
120 | } | |
121 | ||
122 | /*! | |
123 | * Loads a library by specified path with a specified mode. | |
124 | * | |
125 | * \param lib_path Library file name. Can handle std::string, const char*, std::wstring, | |
126 | * const wchar_t* or boost::filesystem::path. | |
127 | * \param mode A mode that will be used on library load. | |
128 | * \param ec Variable that will be set to the result of the operation. | |
129 | * \throw std::bad_alloc in case of insufficient memory. | |
130 | */ | |
131 | shared_library(const boost::filesystem::path& lib_path, boost::system::error_code& ec, load_mode::type mode = load_mode::default_mode) { | |
132 | shared_library::load(lib_path, mode, ec); | |
133 | } | |
134 | ||
135 | //! \overload shared_library(const boost::filesystem::path& lib_path, boost::system::error_code& ec, load_mode::type mode = load_mode::default_mode) | |
136 | shared_library(const boost::filesystem::path& lib_path, load_mode::type mode, boost::system::error_code& ec) { | |
137 | shared_library::load(lib_path, mode, ec); | |
138 | } | |
139 | ||
140 | /*! | |
141 | * Assignment operator. If this->is_loaded() then calls this->unload(). Does not invalidate existing symbols and functions loaded from lib. | |
142 | * | |
143 | * \param lib A shared library to assign from. | |
144 | * \post lib == *this | |
145 | * \throw boost::system::system_error, std::bad_alloc in case of insufficient memory. | |
146 | */ | |
147 | shared_library& operator=(BOOST_COPY_ASSIGN_REF(shared_library) lib) { | |
148 | boost::system::error_code ec; | |
149 | assign(lib, ec); | |
150 | if (ec) { | |
151 | boost::dll::detail::report_error(ec, "boost::dll::shared_library::operator= failed"); | |
152 | } | |
153 | ||
154 | return *this; | |
155 | } | |
156 | ||
157 | /*! | |
158 | * Move assignment operator. If this->is_loaded() then calls this->unload(). Does not invalidate existing symbols and functions loaded from lib. | |
159 | * | |
160 | * \param lib A library to move from. | |
161 | * \post lib.is_loaded() returns false. | |
162 | * \throw Nothing. | |
163 | */ | |
164 | shared_library& operator=(BOOST_RV_REF(shared_library) lib) BOOST_NOEXCEPT { | |
165 | if (lib.native() != native()) { | |
166 | swap(lib); | |
167 | } | |
168 | ||
169 | return *this; | |
170 | } | |
171 | ||
172 | /*! | |
173 | * Destroys the object by calling `unload()`. If library was loaded multiple times | |
174 | * by different instances, the actual DLL/DSO won't be unloaded until | |
175 | * there is at least one instance that references the DLL/DSO. | |
176 | * | |
177 | * \throw Nothing. | |
178 | */ | |
179 | ~shared_library() BOOST_NOEXCEPT {} | |
180 | ||
181 | /*! | |
182 | * Makes *this share the same shared object as lib. If *this is loaded, then unloads it. | |
183 | * | |
184 | * \post lib.location() == this->location(), lib == *this | |
185 | * \param lib A library to copy. | |
186 | * \param ec Variable that will be set to the result of the operation. | |
187 | * \throw std::bad_alloc in case of insufficient memory. | |
188 | */ | |
189 | shared_library& assign(const shared_library& lib, boost::system::error_code& ec) { | |
190 | ec.clear(); | |
191 | ||
192 | if (native() == lib.native()) { | |
193 | return *this; | |
194 | } | |
195 | ||
196 | if (!lib) { | |
197 | unload(); | |
198 | return *this; | |
199 | } | |
200 | ||
201 | boost::filesystem::path loc = lib.location(ec); | |
202 | if (ec) { | |
203 | return *this; | |
204 | } | |
205 | ||
206 | shared_library copy(loc, ec); | |
207 | if (ec) { | |
208 | return *this; | |
209 | } | |
210 | ||
211 | swap(copy); | |
212 | return *this; | |
213 | } | |
214 | ||
215 | /*! | |
216 | * Makes *this share the same shared object as lib. If *this is loaded, then unloads it. | |
217 | * | |
218 | * \param lib A library instance to assign from. | |
219 | * \post lib.location() == this->location() | |
220 | * \throw boost::system::system_error, std::bad_alloc in case of insufficient memory. | |
221 | */ | |
222 | shared_library& assign(const shared_library& lib) { | |
223 | boost::system::error_code ec; | |
224 | assign(lib, ec); | |
225 | if (ec) { | |
226 | boost::dll::detail::report_error(ec, "boost::dll::shared_library::assign() failed"); | |
227 | } | |
228 | ||
229 | return *this; | |
230 | } | |
231 | ||
232 | /*! | |
233 | * Loads a library by specified path with a specified mode. | |
234 | * | |
235 | * Note that if some library is already loaded in this instance, load will | |
236 | * call unload() and then load the new provided library. | |
237 | * | |
238 | * \param lib_path Library file name. Can handle std::string, const char*, std::wstring, | |
239 | * const wchar_t* or boost::filesystem::path. | |
240 | * \param mode A mode that will be used on library load. | |
241 | * \throw boost::system::system_error, std::bad_alloc in case of insufficient memory. | |
242 | * | |
243 | */ | |
244 | void load(const boost::filesystem::path& lib_path, load_mode::type mode = load_mode::default_mode) { | |
245 | boost::system::error_code ec; | |
246 | ||
247 | base_t::load(lib_path, mode, ec); | |
248 | ||
249 | if (ec) { | |
250 | boost::dll::detail::report_error(ec, "boost::dll::shared_library::load() failed"); | |
251 | } | |
252 | } | |
253 | ||
254 | /*! | |
255 | * Loads a library by specified path with a specified mode. | |
256 | * | |
257 | * Note that if some library is already loaded in this instance, load will | |
258 | * call unload() and then load the new provided library. | |
259 | * | |
260 | * \param lib_path Library file name. Can handle std::string, const char*, std::wstring, | |
261 | * const wchar_t* or boost::filesystem::path. | |
262 | * \param ec Variable that will be set to the result of the operation. | |
263 | * \param mode A mode that will be used on library load. | |
264 | * \throw std::bad_alloc in case of insufficient memory. | |
265 | */ | |
266 | void load(const boost::filesystem::path& lib_path, boost::system::error_code& ec, load_mode::type mode = load_mode::default_mode) { | |
267 | ec.clear(); | |
268 | base_t::load(lib_path, mode, ec); | |
269 | } | |
270 | ||
271 | //! \overload void load(const boost::filesystem::path& lib_path, boost::system::error_code& ec, load_mode::type mode = load_mode::default_mode) | |
272 | void load(const boost::filesystem::path& lib_path, load_mode::type mode, boost::system::error_code& ec) { | |
273 | ec.clear(); | |
274 | base_t::load(lib_path, mode, ec); | |
275 | } | |
276 | ||
277 | /*! | |
278 | * Unloads a shared library. If library was loaded multiple times | |
279 | * by different instances, the actual DLL/DSO won't be unloaded until | |
280 | * there is at least one instance that references the DLL/DSO. | |
281 | * | |
282 | * \post this->is_loaded() returns false. | |
283 | * \throw Nothing. | |
284 | */ | |
285 | void unload() BOOST_NOEXCEPT { | |
286 | base_t::unload(); | |
287 | } | |
288 | ||
289 | /*! | |
290 | * Check if an library is loaded. | |
291 | * | |
292 | * \return true if a library has been loaded. | |
293 | * \throw Nothing. | |
294 | */ | |
295 | bool is_loaded() const BOOST_NOEXCEPT { | |
296 | return base_t::is_loaded(); | |
297 | } | |
298 | ||
299 | /*! | |
300 | * Check if an library is not loaded. | |
301 | * | |
302 | * \return true if a library has not been loaded. | |
303 | * \throw Nothing. | |
304 | */ | |
305 | bool operator!() const BOOST_NOEXCEPT { | |
306 | return !is_loaded(); | |
307 | } | |
308 | ||
309 | /*! | |
310 | * Check if an library is loaded. | |
311 | * | |
312 | * \return true if a library has been loaded. | |
313 | * \throw Nothing. | |
314 | */ | |
315 | BOOST_EXPLICIT_OPERATOR_BOOL() | |
316 | ||
317 | /*! | |
318 | * Search for a given symbol on loaded library. Works for all symbols, including alias names. | |
319 | * | |
320 | * \param symbol_name Null-terminated symbol name. Can handle std::string, char*, const char*. | |
321 | * \return `true` if the loaded library contains a symbol with a given name. | |
322 | * \throw Nothing. | |
323 | */ | |
324 | bool has(const char* symbol_name) const BOOST_NOEXCEPT { | |
325 | boost::system::error_code ec; | |
326 | return is_loaded() && !!base_t::symbol_addr(symbol_name, ec) && !ec; | |
327 | } | |
328 | ||
329 | //! \overload bool has(const char* symbol_name) const | |
330 | bool has(const std::string& symbol_name) const BOOST_NOEXCEPT { | |
331 | return has(symbol_name.c_str()); | |
332 | } | |
333 | ||
334 | /*! | |
335 | * Returns reference to the symbol (function or variable) with the given name from the loaded library. | |
336 | * This call will always succeed and throw nothing if call to `has(const char* )` | |
337 | * member function with the same symbol name returned `true`. | |
338 | * | |
339 | * \b Example: | |
340 | * \code | |
341 | * int& i0 = lib.get<int>("integer_name"); | |
342 | * int& i1 = *lib.get<int*>("integer_alias_name"); | |
343 | * \endcode | |
344 | * | |
345 | * \tparam T Type of the symbol that we are going to import. Must be explicitly specified. | |
346 | * \param symbol_name Null-terminated symbol name. Can handle std::string, char*, const char*. | |
347 | * \return Reference to the symbol. | |
348 | * \throw boost::system::system_error if symbol does not exist or if the DLL/DSO was not loaded. | |
349 | */ | |
350 | template <typename T> | |
351 | inline typename boost::enable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T>::type get(const std::string& symbol_name) const { | |
352 | return get<T>(symbol_name.c_str()); | |
353 | } | |
354 | ||
355 | //! \overload T& get(const std::string& symbol_name) const | |
356 | template <typename T> | |
357 | inline typename boost::disable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T&>::type get(const std::string& symbol_name) const { | |
358 | return get<T>(symbol_name.c_str()); | |
359 | } | |
360 | ||
361 | //! \overload T& get(const std::string& symbol_name) const | |
362 | template <typename T> | |
363 | inline typename boost::enable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T>::type get(const char* symbol_name) const { | |
364 | return boost::dll::detail::aggressive_ptr_cast<T>( | |
365 | get_void(symbol_name) | |
366 | ); | |
367 | } | |
368 | ||
369 | //! \overload T& get(const std::string& symbol_name) const | |
370 | template <typename T> | |
371 | inline typename boost::disable_if_c<boost::is_member_pointer<T>::value || boost::is_reference<T>::value, T&>::type get(const char* symbol_name) const { | |
372 | return *boost::dll::detail::aggressive_ptr_cast<T*>( | |
373 | get_void(symbol_name) | |
374 | ); | |
375 | } | |
376 | ||
377 | /*! | |
378 | * Returns a symbol (function or variable) from a shared library by alias name of the symbol. | |
379 | * | |
380 | * \b Example: | |
381 | * \code | |
382 | * int& i = lib.get_alias<int>("integer_alias_name"); | |
383 | * \endcode | |
384 | * | |
385 | * \tparam T Type of the symbol that we are going to import. Must be explicitly specified.. | |
386 | * \param alias_name Null-terminated alias symbol name. Can handle std::string, char*, const char*. | |
387 | * \throw boost::system::system_error if symbol does not exist or if the DLL/DSO was not loaded. | |
388 | */ | |
389 | template <typename T> | |
390 | inline T& get_alias(const char* alias_name) const { | |
391 | return *get<T*>(alias_name); | |
392 | } | |
393 | ||
394 | //! \overload T& get_alias(const char* alias_name) const | |
395 | template <typename T> | |
396 | inline T& get_alias(const std::string& alias_name) const { | |
397 | return *get<T*>(alias_name.c_str()); | |
398 | } | |
399 | ||
400 | private: | |
401 | /// @cond | |
402 | // get_void is required to reduce binary size: it does not depend on a template | |
403 | // parameter and will be instantiated only once. | |
404 | void* get_void(const char* sb) const { | |
405 | boost::system::error_code ec; | |
406 | ||
407 | if (!is_loaded()) { | |
408 | ec = boost::system::error_code( | |
409 | boost::system::errc::bad_file_descriptor, | |
410 | boost::system::generic_category() | |
411 | ); | |
412 | ||
413 | // report_error() calls dlsym, do not use it here! | |
414 | boost::throw_exception( | |
415 | boost::system::system_error( | |
416 | ec, "boost::dll::shared_library::get() failed: no library was loaded" | |
417 | ) | |
418 | ); | |
419 | } | |
420 | ||
421 | void* const ret = base_t::symbol_addr(sb, ec); | |
422 | if (ec || !ret) { | |
423 | boost::dll::detail::report_error(ec, "boost::dll::shared_library::get() failed"); | |
424 | } | |
425 | ||
426 | return ret; | |
427 | } | |
428 | /// @endcond | |
429 | ||
430 | public: | |
431 | ||
432 | /*! | |
433 | * Returns the native handler of the loaded library. | |
434 | * | |
435 | * \return Platform-specific handle. | |
436 | */ | |
437 | native_handle_t native() const BOOST_NOEXCEPT { | |
438 | return base_t::native(); | |
439 | } | |
440 | ||
441 | /*! | |
442 | * Returns full path and name of this shared object. | |
443 | * | |
444 | * \b Example: | |
445 | * \code | |
446 | * shared_library lib("test_lib.dll"); | |
447 | * filesystem::path full_path = lib.location(); // C:\Windows\System32\test_lib.dll | |
448 | * \endcode | |
449 | * | |
450 | * \return Full path to the shared library. | |
451 | * \throw boost::system::system_error, std::bad_alloc. | |
452 | */ | |
453 | boost::filesystem::path location() const { | |
454 | boost::system::error_code ec; | |
455 | if (!is_loaded()) { | |
456 | ec = boost::system::error_code( | |
457 | boost::system::errc::bad_file_descriptor, | |
458 | boost::system::generic_category() | |
459 | ); | |
460 | ||
461 | boost::throw_exception( | |
462 | boost::system::system_error( | |
463 | ec, "boost::dll::shared_library::location() failed (no library was loaded)" | |
464 | ) | |
465 | ); | |
466 | } | |
467 | ||
468 | boost::filesystem::path full_path = base_t::full_module_path(ec); | |
469 | ||
470 | if (ec) { | |
471 | boost::dll::detail::report_error(ec, "boost::dll::shared_library::location() failed"); | |
472 | } | |
473 | ||
474 | return full_path; | |
475 | } | |
476 | ||
477 | /*! | |
478 | * Returns full path and name of shared module. | |
479 | * | |
480 | * \b Example: | |
481 | * \code | |
482 | * shared_library lib("test_lib.dll"); | |
483 | * filesystem::path full_path = lib.location(); // C:\Windows\System32\test_lib.dll | |
484 | * \endcode | |
485 | * | |
486 | * \param ec Variable that will be set to the result of the operation. | |
487 | * \return Full path to the shared library. | |
488 | * \throw std::bad_alloc. | |
489 | */ | |
490 | boost::filesystem::path location(boost::system::error_code& ec) const { | |
491 | if (!is_loaded()) { | |
492 | ec = boost::system::error_code( | |
493 | boost::system::errc::bad_file_descriptor, | |
494 | boost::system::generic_category() | |
495 | ); | |
496 | ||
497 | return boost::filesystem::path(); | |
498 | } | |
499 | ||
500 | ec.clear(); | |
501 | return base_t::full_module_path(ec); | |
502 | } | |
503 | ||
504 | /*! | |
505 | * Returns suffix of shared module: | |
506 | * in a call to load() or the constructor/load. | |
507 | * | |
508 | * \return The suffix od shared module: ".dll" (Windows), ".so" (Unix/Linux/BSD), ".dylib" (MacOS/IOS) | |
509 | */ | |
510 | static boost::filesystem::path suffix() { | |
511 | return base_t::suffix(); | |
512 | } | |
513 | ||
514 | /*! | |
515 | * Swaps two libraries. Does not invalidate existing symbols and functions loaded from libraries. | |
516 | * | |
517 | * \param rhs Library to swap with. | |
518 | * \throw Nothing. | |
519 | */ | |
520 | void swap(shared_library& rhs) BOOST_NOEXCEPT { | |
521 | base_t::swap(rhs); | |
522 | } | |
523 | }; | |
524 | ||
525 | ||
526 | ||
527 | /// Very fast equality check that compares the actual DLL/DSO objects. Throws nothing. | |
528 | inline bool operator==(const shared_library& lhs, const shared_library& rhs) BOOST_NOEXCEPT { | |
529 | return lhs.native() == rhs.native(); | |
530 | } | |
531 | ||
532 | /// Very fast inequality check that compares the actual DLL/DSO objects. Throws nothing. | |
533 | inline bool operator!=(const shared_library& lhs, const shared_library& rhs) BOOST_NOEXCEPT { | |
534 | return lhs.native() != rhs.native(); | |
535 | } | |
536 | ||
537 | /// Compare the actual DLL/DSO objects without any guarantee to be stable between runs. Throws nothing. | |
538 | inline bool operator<(const shared_library& lhs, const shared_library& rhs) BOOST_NOEXCEPT { | |
539 | return lhs.native() < rhs.native(); | |
540 | } | |
541 | ||
542 | /// Swaps two shared libraries. Does not invalidate symbols and functions loaded from libraries. Throws nothing. | |
543 | inline void swap(shared_library& lhs, shared_library& rhs) BOOST_NOEXCEPT { | |
544 | lhs.swap(rhs); | |
545 | } | |
546 | ||
547 | }} // boost::dll | |
548 | ||
549 | #endif // BOOST_DLL_SHARED_LIBRARY_HPP | |
550 |