Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 08:37:33

0001 // Copyright 2014 Renato Tegon Forti, Antony Polukhin.
0002 // Copyright Antony Polukhin, 2015-2025.
0003 //
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE_1_0.txt
0006 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0007 
0008 #ifndef BOOST_DLL_SHARED_LIBRARY_HPP
0009 #define BOOST_DLL_SHARED_LIBRARY_HPP
0010 
0011 /// \file boost/dll/shared_library.hpp
0012 /// \brief Contains the boost::dll::shared_library class, core class for all the
0013 /// DLL/DSO operations.
0014 
0015 #include <boost/dll/config.hpp>
0016 #include <boost/predef/os.h>
0017 #include <boost/core/enable_if.hpp>
0018 #include <boost/core/explicit_operator_bool.hpp>
0019 #include <boost/dll/detail/system_error.hpp>
0020 #include <boost/dll/detail/aggressive_ptr_cast.hpp>
0021 
0022 #if BOOST_OS_WINDOWS
0023 #   include <boost/dll/detail/windows/shared_library_impl.hpp>
0024 #else
0025 #   include <boost/dll/detail/posix/shared_library_impl.hpp>
0026 #endif
0027 
0028 #include <type_traits>
0029 #include <utility>  // std::move
0030 
0031 #ifdef BOOST_HAS_PRAGMA_ONCE
0032 # pragma once
0033 #endif
0034 
0035 namespace boost { namespace dll {
0036 
0037 /*!
0038 * \brief This class can be used to load a
0039 *        Dynamic link libraries (DLL's) or Shared Libraries, also know
0040 *        as dynamic shared objects (DSO's) and get their exported
0041 *        symbols (functions and variables).
0042 *
0043 * shared_library instances share reference count to an actual loaded DLL/DSO, so it
0044 * is safe and memory efficient to have multiple instances of shared_library referencing the same DLL/DSO
0045 * even if those instances were loaded using different paths (relative + absolute) referencing the same object.
0046 *
0047 * On Linux/POSIX link with library "dl". "-fvisibility=hidden" flag is also recommended for use on Linux/POSIX.
0048 */
0049 class shared_library
0050 /// @cond
0051     : private boost::dll::detail::shared_library_impl
0052 /// @endcond
0053 {
0054     typedef boost::dll::detail::shared_library_impl base_t;
0055 
0056 public:
0057 #ifdef BOOST_DLL_DOXYGEN
0058     typedef platform_specific native_handle_t;
0059 #else
0060     typedef shared_library_impl::native_handle_t native_handle_t;
0061 #endif
0062 
0063     /*!
0064     * Creates in anstance that does not reference any DLL/DSO.
0065     *
0066     * \post this->is_loaded() returns false.
0067     * \throw Nothing.
0068     */
0069     shared_library() noexcept = default;
0070 
0071     /*!
0072     * Copy constructor that increments the reference count of an underlying shared library.
0073     * Same as calling constructor with `lib.location()` parameter.
0074     *
0075     * \param lib A library to copy.
0076     * \post lib == *this
0077     * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
0078     */
0079     shared_library(const shared_library& lib)
0080         : base_t()
0081     {
0082         assign(lib);
0083     }
0084 
0085     /*!
0086     * Copy constructor that increments the reference count of an underlying shared library.
0087     * Same as calling constructor with `lib.location(), ec` parameters.
0088     *
0089     * \param lib A shared library to copy.
0090     * \param ec Variable that will be set to the result of the operation.
0091     * \post lib == *this
0092     * \throw std::bad_alloc in case of insufficient memory.
0093     */
0094     shared_library(const shared_library& lib, std::error_code& ec)
0095         : base_t()
0096     {
0097         assign(lib, ec);
0098     }
0099 
0100     /*!
0101     * Move constructor. Does not invalidate existing symbols and functions loaded from lib.
0102     *
0103     * \param lib A shared library to move from.
0104     * \post lib.is_loaded() returns false, this->is_loaded() return true.
0105     * \throw Nothing.
0106     */
0107     shared_library(shared_library&& lib) noexcept
0108         : base_t(std::move(lib))
0109     {}
0110 
0111     /*!
0112     * Loads a library by specified path with a specified mode.
0113     *
0114     * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
0115     *           const wchar_t* or \forcedlinkfs{path}.
0116     * \param mode A mode that will be used on library load.
0117     * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
0118     */
0119     explicit shared_library(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) {
0120         shared_library::load(lib_path, mode);
0121     }
0122 
0123     /*!
0124     * Loads a library by specified path with a specified mode.
0125     *
0126     * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
0127     *           const wchar_t* or \forcedlinkfs{path}.
0128     * \param mode A mode that will be used on library load.
0129     * \param ec Variable that will be set to the result of the operation.
0130     * \throw std::bad_alloc in case of insufficient memory.
0131     */
0132     shared_library(const boost::dll::fs::path& lib_path, std::error_code& ec, load_mode::type mode = load_mode::default_mode) {
0133         shared_library::load(lib_path, mode, ec);
0134     }
0135 
0136     //! \overload shared_library(const boost::dll::fs::path& lib_path, std::error_code& ec, load_mode::type mode = load_mode::default_mode)
0137     shared_library(const boost::dll::fs::path& lib_path, load_mode::type mode, std::error_code& ec) {
0138         shared_library::load(lib_path, mode, ec);
0139     }
0140 
0141     /*!
0142     * Takes ownership of a loaded library.
0143     *
0144     * \param handle The native handle.
0145     */
0146     explicit shared_library(native_handle_t handle) noexcept
0147         : base_t(handle)
0148     {}
0149 
0150     /*!
0151     * Assignment operator. If this->is_loaded() then calls this->unload(). Does not invalidate existing symbols and functions loaded from lib.
0152     *
0153     * \param lib A shared library to assign from.
0154     * \post lib == *this
0155     * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
0156     */
0157     shared_library& operator=(const shared_library& lib) {
0158         std::error_code ec;
0159         assign(lib, ec);
0160         if (ec) {
0161             boost::dll::detail::report_error(ec, "boost::dll::shared_library::operator= failed");
0162         }
0163 
0164         return *this;
0165     }
0166 
0167     /*!
0168     * Move assignment operator. If this->is_loaded() then calls this->unload(). Does not invalidate existing symbols and functions loaded from lib.
0169     *
0170     * \param lib A library to move from.
0171     * \post lib.is_loaded() returns false.
0172     * \throw Nothing.
0173     */
0174     shared_library& operator=(shared_library&& lib) noexcept {
0175         if (lib.native() != native()) {
0176             swap(lib);
0177         }
0178 
0179         return *this;
0180     }
0181 
0182     /*!
0183     * Destroys the object by calling `unload()`. If library was loaded multiple times
0184     * by different instances, the actual DLL/DSO won't be unloaded until
0185     * there is at least one instance that references the DLL/DSO.
0186     *
0187     * \throw Nothing.
0188     */
0189     ~shared_library() = default;
0190 
0191     /*!
0192     * Makes *this share the same shared object as lib. If *this is loaded, then unloads it.
0193     *
0194     * \post lib.location() == this->location(), lib == *this
0195     * \param lib A library to copy.
0196     * \param ec Variable that will be set to the result of the operation.
0197     * \throw std::bad_alloc in case of insufficient memory.
0198     */
0199     shared_library& assign(const shared_library& lib, std::error_code& ec) {
0200         ec.clear();
0201 
0202         if (native() == lib.native()) {
0203             return *this;
0204         }
0205 
0206         if (!lib) {
0207             unload();
0208             return *this;
0209         }
0210 
0211         boost::dll::fs::path loc = lib.location(ec);
0212         if (ec) {
0213             return *this;
0214         }
0215 
0216         shared_library copy(loc, ec);
0217         if (ec) {
0218             return *this;
0219         }
0220 
0221         swap(copy);
0222         return *this;
0223     }
0224 
0225     /*!
0226     * Makes *this share the same shared object as lib. If *this is loaded, then unloads it.
0227     *
0228     * \param lib A library instance to assign from.
0229     * \post lib.location() == this->location()
0230     * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
0231     */
0232     shared_library& assign(const shared_library& lib) {
0233         std::error_code ec;
0234         assign(lib, ec);
0235         if (ec) {
0236             boost::dll::detail::report_error(ec, "boost::dll::shared_library::assign() failed");
0237         }
0238 
0239         return *this;
0240     }
0241 
0242     /*!
0243     * Loads a library by specified path with a specified mode.
0244     *
0245     * Note that if some library is already loaded in this instance, load will
0246     * call unload() and then load the new provided library.
0247     *
0248     * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
0249     *           const wchar_t* or \forcedlinkfs{path}.
0250     * \param mode A mode that will be used on library load.
0251     * \throw \forcedlinkfs{system_error}, std::bad_alloc in case of insufficient memory.
0252     *
0253     */
0254     void load(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) {
0255         std::error_code ec;
0256 
0257         base_t::load(lib_path, mode, ec);
0258 
0259         if (ec) {
0260             boost::dll::detail::report_error(ec, "boost::dll::shared_library::load() failed");
0261         }
0262     }
0263 
0264     /*!
0265     * Loads a library by specified path with a specified mode.
0266     *
0267     * Note that if some library is already loaded in this instance, load will
0268     * call unload() and then load the new provided library.
0269     *
0270     * \param lib_path Library file name. Can handle std::string, const char*, std::wstring,
0271     *           const wchar_t* or \forcedlinkfs{path}.
0272     * \param ec Variable that will be set to the result of the operation.
0273     * \param mode A mode that will be used on library load.
0274     * \throw std::bad_alloc in case of insufficient memory.
0275     */
0276     void load(const boost::dll::fs::path& lib_path, std::error_code& ec, load_mode::type mode = load_mode::default_mode) {
0277         ec.clear();
0278         base_t::load(lib_path, mode, ec);
0279     }
0280 
0281     //! \overload void load(const boost::dll::fs::path& lib_path, std::error_code& ec, load_mode::type mode = load_mode::default_mode)
0282     void load(const boost::dll::fs::path& lib_path, load_mode::type mode, std::error_code& ec) {
0283         ec.clear();
0284         base_t::load(lib_path, mode, ec);
0285     }
0286 
0287     /*!
0288     * Unloads a shared library.  If library was loaded multiple times
0289     * by different instances, the actual DLL/DSO won't be unloaded until
0290     * there is at least one instance that references the DLL/DSO.
0291     *
0292     * \post this->is_loaded() returns false.
0293     * \throw Nothing.
0294     */
0295     void unload() noexcept {
0296         base_t::unload();
0297     }
0298 
0299     /*!
0300     * Check if an library is loaded.
0301     *
0302     * \return true if a library has been loaded.
0303     * \throw Nothing.
0304     */
0305     bool is_loaded() const noexcept {
0306         return base_t::is_loaded();
0307     }
0308 
0309     /*!
0310     * Check if an library is loaded.
0311     *
0312     * \return true if a library has been loaded.
0313     * \throw Nothing.
0314     */
0315     explicit operator bool() const noexcept {
0316         return is_loaded();
0317     }
0318 
0319     /*!
0320     * Search for a given symbol on loaded library. Works for all symbols, including alias names.
0321     *
0322     * \param symbol_name Null-terminated symbol name. Can handle std::string, char*, const char*.
0323     * \return `true` if the loaded library contains a symbol with a given name.
0324     * \throw Nothing.
0325     */
0326     bool has(const char* symbol_name) const noexcept {
0327         std::error_code ec;
0328         return is_loaded() && !!base_t::symbol_addr(symbol_name, ec) && !ec;
0329     }
0330 
0331     //! \overload bool has(const char* symbol_name) const
0332     bool has(const std::string& symbol_name) const noexcept {
0333         return has(symbol_name.c_str());
0334     }
0335 
0336     /*!
0337     * Returns reference to the symbol (function or variable) with the given name from the loaded library.
0338     * This call will always succeed and throw nothing if call to `has(const char* )`
0339     * member function with the same symbol name returned `true`.
0340     *
0341     * \b Example:
0342     * \code
0343     * int& i0 = lib.get<int>("integer_name");
0344     * int& i1 = *lib.get<int*>("integer_alias_name");
0345     * \endcode
0346     *
0347     * \tparam T Type of the symbol that we are going to import. Must be explicitly specified.
0348     * \param symbol_name Null-terminated symbol name. Can handle std::string, char*, const char*.
0349     * \return Reference to the symbol.
0350     * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded.
0351     */
0352     template <typename T>
0353     inline typename std::enable_if<std::is_member_pointer<T>::value || std::is_reference<T>::value, T>::type  get(const std::string& symbol_name) const {
0354         return get<T>(symbol_name.c_str());
0355     }
0356 
0357     //! \overload T& get(const std::string& symbol_name) const
0358     template <typename T>
0359     inline typename std::enable_if<!(std::is_member_pointer<T>::value || std::is_reference<T>::value), T&>::type get(const std::string& symbol_name) const {
0360         return get<T>(symbol_name.c_str());
0361     }
0362 
0363     //! \overload T& get(const std::string& symbol_name) const
0364     template <typename T>
0365     inline typename std::enable_if<std::is_member_pointer<T>::value || std::is_reference<T>::value, T>::type get(const char* symbol_name) const {
0366         return boost::dll::detail::aggressive_ptr_cast<T>(
0367             get_void(symbol_name)
0368         );
0369     }
0370 
0371     //! \overload T& get(const std::string& symbol_name) const
0372     template <typename T>
0373     inline typename std::enable_if<!(std::is_member_pointer<T>::value || std::is_reference<T>::value), T&>::type get(const char* symbol_name) const {
0374         return *boost::dll::detail::aggressive_ptr_cast<T*>(
0375             get_void(symbol_name)
0376         );
0377     }
0378 
0379     /*!
0380     * Returns a symbol (function or variable) from a shared library by alias name of the symbol.
0381     *
0382     * \b Example:
0383     * \code
0384     * int& i = lib.get_alias<int>("integer_alias_name");
0385     * \endcode
0386     *
0387     * \tparam T Type of the symbol that we are going to import. Must be explicitly specified..
0388     * \param alias_name Null-terminated alias symbol name. Can handle std::string, char*, const char*.
0389     * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded.
0390     */
0391     template <typename T>
0392     inline T& get_alias(const char* alias_name) const {
0393         return *get<T*>(alias_name);
0394     }
0395 
0396     //! \overload T& get_alias(const char* alias_name) const
0397     template <typename T>
0398     inline T& get_alias(const std::string& alias_name) const {
0399         return *get<T*>(alias_name.c_str());
0400     }
0401 
0402 private:
0403     /// @cond
0404     // get_void is required to reduce binary size: it does not depend on a template
0405     // parameter and will be instantiated only once.
0406     void* get_void(const char* sb) const {
0407         std::error_code ec;
0408 
0409         if (!is_loaded()) {
0410             ec = std::make_error_code(
0411                 std::errc::bad_file_descriptor
0412             );
0413 
0414             // report_error() calls dlsym, do not use it here!
0415             boost::throw_exception(
0416                 boost::dll::fs::system_error(
0417                     ec, "boost::dll::shared_library::get() failed: no library was loaded"
0418                 )
0419             );
0420         }
0421 
0422         void* const ret = base_t::symbol_addr(sb, ec);
0423         if (ec || !ret) {
0424             boost::dll::detail::report_error(ec, "boost::dll::shared_library::get() failed");
0425         }
0426 
0427         return ret;
0428     }
0429     /// @endcond
0430 
0431 public:
0432 
0433     /*!
0434     * Returns the native handler of the loaded library.
0435     *
0436     * \return Platform-specific handle.
0437     */
0438     native_handle_t native() const noexcept {
0439         return base_t::native();
0440     }
0441 
0442    /*!
0443     * Returns full path and name of this shared object.
0444     *
0445     * \b Example:
0446     * \code
0447     * shared_library lib("test_lib.dll");
0448     * filesystem::path full_path = lib.location(); // C:\Windows\System32\test_lib.dll
0449     * \endcode
0450     *
0451     * \return Full path to the shared library.
0452     * \throw \forcedlinkfs{system_error}, std::bad_alloc.
0453     */
0454     boost::dll::fs::path location() const {
0455         std::error_code ec;
0456         if (!is_loaded()) {
0457             ec = std::make_error_code(
0458                 std::errc::bad_file_descriptor
0459             );
0460 
0461             boost::throw_exception(
0462                 boost::dll::fs::system_error(
0463                     ec, "boost::dll::shared_library::location() failed (no library was loaded)"
0464                 )
0465             );
0466         }
0467 
0468         boost::dll::fs::path full_path = base_t::full_module_path(ec);
0469 
0470         if (ec) {
0471             boost::dll::detail::report_error(ec, "boost::dll::shared_library::location() failed");
0472         }
0473 
0474         return full_path;
0475     }
0476 
0477    /*!
0478     * Returns full path and name of shared module.
0479     *
0480     * \b Example:
0481     * \code
0482     * shared_library lib("test_lib.dll");
0483     * filesystem::path full_path = lib.location(); // C:\Windows\System32\test_lib.dll
0484     * \endcode
0485     *
0486     * \param ec Variable that will be set to the result of the operation.
0487     * \return Full path to the shared library.
0488     * \throw std::bad_alloc.
0489     */
0490     boost::dll::fs::path location(std::error_code& ec) const {
0491         if (!is_loaded()) {
0492             ec = std::make_error_code(
0493                 std::errc::bad_file_descriptor
0494             );
0495 
0496             return boost::dll::fs::path();
0497         }
0498 
0499         ec.clear();
0500         return base_t::full_module_path(ec);
0501     }
0502 
0503     /*!
0504     * Returns suffix of shared module:
0505     * in a call to load() or the constructor/load.
0506     *
0507     * \return The suffix od shared module: ".dll" (Windows), ".so" (Unix/Linux/BSD), ".dylib" (MacOS/IOS)
0508     */
0509     static boost::dll::fs::path suffix() {
0510         return base_t::suffix();
0511     }
0512 
0513     /*!
0514     * Returns the decorated path to a shared module name, i.e. with needed prefix/suffix added.
0515     *
0516     * \b Recommendations: Use `load` with `load_mode::append_decorations` instead of constructing the decorated path via `decorate()` and loading by it.
0517     *
0518     * For instance, for a path like "path/to/boost" it returns :
0519     * - path/to/libboost.so on posix platforms
0520     * - path/to/libboost.dylib on OSX
0521     * - path/to/boost.dll on Windows
0522     *
0523     * Method handles both relative and absolute paths.
0524     *
0525     * - Windows note: `decorate()` does not prepend "lib" to the decorated path. Use `load` with `load_mode::append_decorations` for MinGW compatibility purpose.
0526     * - Posix note: if the initial module name is already prepended with lib, only the suffix() is appended to the path
0527     *
0528     * \param sl the module name and path to decorate - for instance : /usr/lib/boost
0529     *
0530     * \return The decorated unportable path that may not exists in the filesystem or could be wrong due to platform specifics.
0531     */
0532     static boost::dll::fs::path decorate(const boost::dll::fs::path& sl) {
0533         return base_t::decorate(sl);
0534     }
0535 
0536     /*!
0537     * Swaps two libraries. Does not invalidate existing symbols and functions loaded from libraries.
0538     *
0539     * \param rhs Library to swap with.
0540     * \throw Nothing.
0541     */
0542     void swap(shared_library& rhs) noexcept {
0543         base_t::swap(rhs);
0544     }
0545 };
0546 
0547 
0548 
0549 /// Very fast equality check that compares the actual DLL/DSO objects. Throws nothing.
0550 inline bool operator==(const shared_library& lhs, const shared_library& rhs) noexcept {
0551     return lhs.native() == rhs.native();
0552 }
0553 
0554 /// Very fast inequality check that compares the actual DLL/DSO objects. Throws nothing.
0555 inline bool operator!=(const shared_library& lhs, const shared_library& rhs) noexcept {
0556     return lhs.native() != rhs.native();
0557 }
0558 
0559 /// Compare the actual DLL/DSO objects without any guarantee to be stable between runs. Throws nothing.
0560 inline bool operator<(const shared_library& lhs, const shared_library& rhs) noexcept {
0561     return lhs.native() < rhs.native();
0562 }
0563 
0564 /// Swaps two shared libraries. Does not invalidate symbols and functions loaded from libraries. Throws nothing.
0565 inline void swap(shared_library& lhs, shared_library& rhs) noexcept {
0566     lhs.swap(rhs);
0567 }
0568 
0569 }} // boost::dll
0570 
0571 #endif // BOOST_DLL_SHARED_LIBRARY_HPP