| 1 | // Copyright 2016 Klemens Morgenstern |
| 2 | // Copyright Antony Polukhin, 2019-2024 |
| 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_SMART_LIBRARY_HPP_ |
| 9 | #define BOOST_DLL_SMART_LIBRARY_HPP_ |
| 10 | |
| 11 | /// \file boost/dll/smart_library.hpp |
| 12 | /// \warning Extremely experimental! Requires C++11! Will change in next version of Boost! boost/dll/smart_library.hpp is not included in boost/dll.hpp |
| 13 | /// \brief Contains the boost::dll::experimental::smart_library class for loading mangled symbols. |
| 14 | |
| 15 | #include <boost/dll/config.hpp> |
| 16 | #if defined(_MSC_VER) // MSVC, Clang-cl, and ICC on Windows |
| 17 | # include <boost/dll/detail/demangling/msvc.hpp> |
| 18 | #else |
| 19 | # include <boost/dll/detail/demangling/itanium.hpp> |
| 20 | #endif |
| 21 | |
| 22 | #if (__cplusplus < 201103L) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201103L) |
| 23 | # error This file requires C++11 at least! |
| 24 | #endif |
| 25 | |
| 26 | #include <boost/dll/shared_library.hpp> |
| 27 | #include <boost/dll/detail/get_mem_fn_type.hpp> |
| 28 | #include <boost/dll/detail/ctor_dtor.hpp> |
| 29 | #include <boost/dll/detail/type_info.hpp> |
| 30 | #include <boost/type_traits/is_object.hpp> |
| 31 | #include <boost/type_traits/is_void.hpp> |
| 32 | #include <boost/type_traits/is_function.hpp> |
| 33 | |
| 34 | |
| 35 | |
| 36 | namespace boost { |
| 37 | namespace dll { |
| 38 | namespace experimental { |
| 39 | |
| 40 | using boost::dll::detail::constructor; |
| 41 | using boost::dll::detail::destructor; |
| 42 | |
| 43 | /*! |
| 44 | * \brief This class is an extension of \ref shared_library, which allows to load C++ symbols. |
| 45 | * |
| 46 | * This class allows type safe loading of overloaded functions, member-functions, constructors and variables. |
| 47 | * It also allows to overwrite classes so they can be loaded, while being declared with different names. |
| 48 | * |
| 49 | * \warning Is still very experimental. |
| 50 | * |
| 51 | * Currently known limitations: |
| 52 | * |
| 53 | * Member functions must be defined outside of the class to be exported. That is: |
| 54 | * \code |
| 55 | * //not exported: |
| 56 | * struct BOOST_SYMBOL_EXPORT my_class { void func() {}}; |
| 57 | * //exported |
| 58 | * struct BOOST_SYMBOL_EXPORT my_class { void func();}; |
| 59 | * void my_class::func() {}; |
| 60 | * \endcode |
| 61 | * |
| 62 | * With the current analysis, the first version does get exported in MSVC. |
| 63 | * MinGW also does export it, BOOST_SYMBOL_EXPORT is written before it. To allow this on windows one can use |
| 64 | * BOOST_DLL_MEMBER_EXPORT for this, so that MinGW and MSVC can provide those functions. This does however not work with gcc on linux. |
| 65 | * |
| 66 | * Direct initialization of members. |
| 67 | * On linux the following member variable i will not be initialized when using the allocating constructor: |
| 68 | * \code |
| 69 | * struct BOOST_SYMBOL_EXPORT my_class { int i; my_class() : i(42) {} }; |
| 70 | * \endcode |
| 71 | * |
| 72 | * This does however not happen when the value is set inside the constructor function. |
| 73 | */ |
| 74 | class smart_library { |
| 75 | shared_library _lib; |
| 76 | detail::mangled_storage_impl _storage; |
| 77 | |
| 78 | public: |
| 79 | /*! |
| 80 | * Get the underlying shared_library |
| 81 | */ |
| 82 | const shared_library &shared_lib() const {return _lib;} |
| 83 | |
| 84 | using mangled_storage = detail::mangled_storage_impl; |
| 85 | /*! |
| 86 | * Access to the mangled storage, which is created on construction. |
| 87 | * |
| 88 | * \throw Nothing. |
| 89 | */ |
| 90 | const mangled_storage &symbol_storage() const {return _storage;} |
| 91 | |
| 92 | ///Overload, for current development. |
| 93 | mangled_storage &symbol_storage() {return _storage;} |
| 94 | |
| 95 | //! \copydoc shared_library::shared_library() |
| 96 | smart_library() BOOST_NOEXCEPT {}; |
| 97 | |
| 98 | //! \copydoc shared_library::shared_library(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) |
| 99 | smart_library(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) { |
| 100 | _lib.load(lib_path, mode); |
| 101 | _storage.load(library_path: lib_path); |
| 102 | } |
| 103 | |
| 104 | //! \copydoc shared_library::shared_library(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode) |
| 105 | smart_library(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode) { |
| 106 | load(lib_path, mode, ec); |
| 107 | } |
| 108 | |
| 109 | //! \copydoc shared_library::shared_library(const boost::dll::fs::path& lib_path, load_mode::type mode, boost::dll::fs::error_code& ec) |
| 110 | smart_library(const boost::dll::fs::path& lib_path, load_mode::type mode, boost::dll::fs::error_code& ec) { |
| 111 | load(lib_path, mode, ec); |
| 112 | } |
| 113 | /*! |
| 114 | * copy a smart_library object. |
| 115 | * |
| 116 | * \param lib A smart_library to move from. |
| 117 | * |
| 118 | * \throw Nothing. |
| 119 | */ |
| 120 | smart_library(const smart_library & lib) BOOST_NOEXCEPT |
| 121 | : _lib(lib._lib), _storage(lib._storage) |
| 122 | {} |
| 123 | /*! |
| 124 | * Move a smart_library object. |
| 125 | * |
| 126 | * \param lib A smart_library to move from. |
| 127 | * |
| 128 | * \throw Nothing. |
| 129 | */ |
| 130 | smart_library(BOOST_RV_REF(smart_library) lib) BOOST_NOEXCEPT |
| 131 | : _lib(boost::move(t&: lib._lib)), _storage(boost::move(t&: lib._storage)) |
| 132 | {} |
| 133 | |
| 134 | /*! |
| 135 | * Construct from a shared_library object. |
| 136 | * |
| 137 | * \param lib A shared_library to move from. |
| 138 | * |
| 139 | * \throw Nothing. |
| 140 | */ |
| 141 | explicit smart_library(const shared_library & lib) BOOST_NOEXCEPT |
| 142 | : _lib(lib) |
| 143 | { |
| 144 | _storage.load(library_path: lib.location()); |
| 145 | } |
| 146 | /*! |
| 147 | * Construct from a shared_library object. |
| 148 | * |
| 149 | * \param lib A shared_library to move from. |
| 150 | * |
| 151 | * \throw Nothing. |
| 152 | */ |
| 153 | explicit smart_library(BOOST_RV_REF(shared_library) lib) BOOST_NOEXCEPT |
| 154 | : _lib(boost::move(t&: static_cast<shared_library&>(lib))) |
| 155 | { |
| 156 | _storage.load(library_path: lib.location()); |
| 157 | } |
| 158 | |
| 159 | /*! |
| 160 | * Destroys the smart_library. |
| 161 | * `unload()` is called if the DLL/DSO was loaded. If library was loaded multiple times |
| 162 | * by different instances of shared_library, the actual DLL/DSO won't be unloaded until |
| 163 | * there is at least one instance of shared_library. |
| 164 | * |
| 165 | * \throw Nothing. |
| 166 | */ |
| 167 | ~smart_library() BOOST_NOEXCEPT {}; |
| 168 | |
| 169 | //! \copydoc shared_library::load(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) |
| 170 | void load(const boost::dll::fs::path& lib_path, load_mode::type mode = load_mode::default_mode) { |
| 171 | boost::dll::fs::error_code ec; |
| 172 | _storage.load(library_path: lib_path); |
| 173 | _lib.load(lib_path, mode, ec); |
| 174 | |
| 175 | if (ec) { |
| 176 | boost::dll::detail::report_error(ec, message: "load() failed" ); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | //! \copydoc shared_library::load(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode) |
| 181 | void load(const boost::dll::fs::path& lib_path, boost::dll::fs::error_code& ec, load_mode::type mode = load_mode::default_mode) { |
| 182 | ec.clear(); |
| 183 | _storage.load(library_path: lib_path); |
| 184 | _lib.load(lib_path, mode, ec); |
| 185 | } |
| 186 | |
| 187 | //! \copydoc shared_library::load(const boost::dll::fs::path& lib_path, load_mode::type mode, boost::dll::fs::error_code& ec) |
| 188 | void load(const boost::dll::fs::path& lib_path, load_mode::type mode, boost::dll::fs::error_code& ec) { |
| 189 | ec.clear(); |
| 190 | _storage.load(library_path: lib_path); |
| 191 | _lib.load(lib_path, mode, ec); |
| 192 | } |
| 193 | |
| 194 | /*! |
| 195 | * Load a variable from the referenced library. |
| 196 | * |
| 197 | * Unlinke shared_library::get this function will also load scoped variables, which also includes static class members. |
| 198 | * |
| 199 | * \note When mangled, MSVC will also check the type. |
| 200 | * |
| 201 | * \param name Name of the variable |
| 202 | * \tparam T Type of the variable |
| 203 | * \return A reference to the variable of type T. |
| 204 | * |
| 205 | * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. |
| 206 | */ |
| 207 | template<typename T> |
| 208 | T& get_variable(const std::string &name) const { |
| 209 | return _lib.get<T>(_storage.get_variable<T>(name)); |
| 210 | } |
| 211 | |
| 212 | /*! |
| 213 | * Load a function from the referenced library. |
| 214 | * |
| 215 | * \b Example: |
| 216 | * |
| 217 | * \code |
| 218 | * smart_library lib("test_lib.so"); |
| 219 | * typedef int (&add_ints)(int, int); |
| 220 | * typedef double (&add_doubles)(double, double); |
| 221 | * add_ints f1 = lib.get_function<int(int, int)> ("func_name"); |
| 222 | * add_doubles f2 = lib.get_function<double(double, double)>("func_name"); |
| 223 | * \endcode |
| 224 | * |
| 225 | * \note When mangled, MSVC will also check the return type. |
| 226 | * |
| 227 | * \param name Name of the function. |
| 228 | * \tparam Func Type of the function, required for determining the overload |
| 229 | * \return A reference to the function of type F. |
| 230 | * |
| 231 | * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. |
| 232 | */ |
| 233 | template<typename Func> |
| 234 | Func& get_function(const std::string &name) const { |
| 235 | return _lib.get<Func>(_storage.get_function<Func>(name)); |
| 236 | } |
| 237 | |
| 238 | /*! |
| 239 | * Load a member-function from the referenced library. |
| 240 | * |
| 241 | * \b Example (import class is MyClass, which is available inside the library and the host): |
| 242 | * |
| 243 | * \code |
| 244 | * smart_library lib("test_lib.so"); |
| 245 | * |
| 246 | * typedef int MyClass(*func)(int); |
| 247 | * typedef int MyClass(*func_const)(int) const; |
| 248 | * |
| 249 | * add_ints f1 = lib.get_mem_fn<MyClass, int(int)> ("MyClass::function"); |
| 250 | * add_doubles f2 = lib.get_mem_fn<const MyClass, double(double)>("MyClass::function"); |
| 251 | * \endcode |
| 252 | * |
| 253 | * \note When mangled, MSVC will also check the return type. |
| 254 | * |
| 255 | * \param name Name of the function. |
| 256 | * \tparam Class The class the function is a member of. If Class is const, the function will be assumed as taking a const this-pointer. The same applies for volatile. |
| 257 | * \tparam Func Signature of the function, required for determining the overload |
| 258 | * \return A pointer to the member-function with the signature provided |
| 259 | * |
| 260 | * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. |
| 261 | */ |
| 262 | template<typename Class, typename Func> |
| 263 | typename boost::dll::detail::get_mem_fn_type<Class, Func>::mem_fn get_mem_fn(const std::string& name) const { |
| 264 | return _lib.get<typename boost::dll::detail::get_mem_fn_type<Class, Func>::mem_fn>( |
| 265 | _storage.get_mem_fn<Class, Func>(name) |
| 266 | ); |
| 267 | } |
| 268 | |
| 269 | /*! |
| 270 | * Load a constructor from the referenced library. |
| 271 | * |
| 272 | * \b Example (import class is MyClass, which is available inside the library and the host): |
| 273 | * |
| 274 | * \code |
| 275 | * smart_library lib("test_lib.so"); |
| 276 | * |
| 277 | * constructor<MyClass(int) f1 = lib.get_mem_fn<MyClass(int)>(); |
| 278 | * \endcode |
| 279 | * |
| 280 | * \tparam Signature Signature of the function, required for determining the overload. The return type is the class which this is the constructor of. |
| 281 | * \return A constructor object. |
| 282 | * |
| 283 | * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. |
| 284 | */ |
| 285 | template<typename Signature> |
| 286 | constructor<Signature> get_constructor() const { |
| 287 | return boost::dll::detail::load_ctor<Signature>(_lib, _storage.get_constructor<Signature>()); |
| 288 | } |
| 289 | |
| 290 | /*! |
| 291 | * Load a destructor from the referenced library. |
| 292 | * |
| 293 | * \b Example (import class is MyClass, which is available inside the library and the host): |
| 294 | * |
| 295 | * \code |
| 296 | * smart_library lib("test_lib.so"); |
| 297 | * |
| 298 | * destructor<MyClass> f1 = lib.get_mem_fn<MyClass>(); |
| 299 | * \endcode |
| 300 | * |
| 301 | * \tparam Class The class whose destructor shall be loaded |
| 302 | * \return A destructor object. |
| 303 | * |
| 304 | * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. |
| 305 | * |
| 306 | */ |
| 307 | template<typename Class> |
| 308 | destructor<Class> get_destructor() const { |
| 309 | return boost::dll::detail::load_dtor<Class>(_lib, _storage.get_destructor<Class>()); |
| 310 | } |
| 311 | /*! |
| 312 | * Load the typeinfo of the given type. |
| 313 | * |
| 314 | * \b Example (import class is MyClass, which is available inside the library and the host): |
| 315 | * |
| 316 | * \code |
| 317 | * smart_library lib("test_lib.so"); |
| 318 | * |
| 319 | * std::type_info &ti = lib.get_Type_info<MyClass>(); |
| 320 | * \endcode |
| 321 | * |
| 322 | * \tparam Class The class whose typeinfo shall be loaded |
| 323 | * \return A reference to a type_info object. |
| 324 | * |
| 325 | * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. |
| 326 | * |
| 327 | */ |
| 328 | template<typename Class> |
| 329 | const std::type_info& get_type_info() const |
| 330 | { |
| 331 | return boost::dll::detail::load_type_info<Class>(_lib, _storage); |
| 332 | } |
| 333 | /** |
| 334 | * This function can be used to add a type alias. |
| 335 | * |
| 336 | * This is to be used, when a class shall be imported, which is not declared on the host side. |
| 337 | * |
| 338 | * Example: |
| 339 | * \code |
| 340 | * smart_library lib("test_lib.so"); |
| 341 | * |
| 342 | * lib.add_type_alias<MyAlias>("MyClass"); //when using MyAlias, the library will look for MyClass |
| 343 | * |
| 344 | * //get the destructor of MyClass |
| 345 | * destructor<MyAlias> dtor = lib.get_destructor<MyAlias>(); |
| 346 | * \endcode |
| 347 | * |
| 348 | * |
| 349 | * \param name Name of the class the alias is for. |
| 350 | * |
| 351 | * \attention If the alias-type is not large enough for the imported class, it will result in undefined behaviour. |
| 352 | * \warning The alias will only be applied for the type signature, it will not replace the token in the scoped name. |
| 353 | */ |
| 354 | template<typename Alias> void add_type_alias(const std::string& name) { |
| 355 | this->_storage.add_alias<Alias>(name); |
| 356 | } |
| 357 | |
| 358 | //! \copydoc shared_library::unload() |
| 359 | void unload() BOOST_NOEXCEPT { |
| 360 | _storage.clear(); |
| 361 | _lib.unload(); |
| 362 | } |
| 363 | |
| 364 | //! \copydoc shared_library::is_loaded() const |
| 365 | bool is_loaded() const BOOST_NOEXCEPT { |
| 366 | return _lib.is_loaded(); |
| 367 | } |
| 368 | |
| 369 | //! \copydoc shared_library::operator!() const |
| 370 | bool operator!() const BOOST_NOEXCEPT { |
| 371 | return !is_loaded(); |
| 372 | } |
| 373 | |
| 374 | //! \copydoc shared_library::operator bool() const |
| 375 | BOOST_EXPLICIT_OPERATOR_BOOL() |
| 376 | |
| 377 | //! \copydoc shared_library::has(const char* symbol_name) const |
| 378 | bool has(const char* symbol_name) const BOOST_NOEXCEPT { |
| 379 | return _lib.has(symbol_name); |
| 380 | } |
| 381 | |
| 382 | //! \copydoc shared_library::has(const std::string& symbol_name) const |
| 383 | bool has(const std::string& symbol_name) const BOOST_NOEXCEPT { |
| 384 | return _lib.has(symbol_name); |
| 385 | } |
| 386 | |
| 387 | //! \copydoc shared_library::assign(const shared_library& lib) |
| 388 | smart_library& assign(const smart_library& lib) { |
| 389 | _lib.assign(lib: lib._lib); |
| 390 | _storage.assign(storage: lib._storage); |
| 391 | return *this; |
| 392 | } |
| 393 | |
| 394 | //! \copydoc shared_library::swap(shared_library& rhs) |
| 395 | void swap(smart_library& rhs) BOOST_NOEXCEPT { |
| 396 | _lib.swap(rhs&: rhs._lib); |
| 397 | _storage.swap(storage&: rhs._storage); |
| 398 | } |
| 399 | }; |
| 400 | |
| 401 | /// Very fast equality check that compares the actual DLL/DSO objects. Throws nothing. |
| 402 | inline bool operator==(const smart_library& lhs, const smart_library& rhs) BOOST_NOEXCEPT { |
| 403 | return lhs.shared_lib().native() == rhs.shared_lib().native(); |
| 404 | } |
| 405 | |
| 406 | /// Very fast inequality check that compares the actual DLL/DSO objects. Throws nothing. |
| 407 | inline bool operator!=(const smart_library& lhs, const smart_library& rhs) BOOST_NOEXCEPT { |
| 408 | return lhs.shared_lib().native() != rhs.shared_lib().native(); |
| 409 | } |
| 410 | |
| 411 | /// Compare the actual DLL/DSO objects without any guarantee to be stable between runs. Throws nothing. |
| 412 | inline bool operator<(const smart_library& lhs, const smart_library& rhs) BOOST_NOEXCEPT { |
| 413 | return lhs.shared_lib().native() < rhs.shared_lib().native(); |
| 414 | } |
| 415 | |
| 416 | /// Swaps two shared libraries. Does not invalidate symbols and functions loaded from libraries. Throws nothing. |
| 417 | inline void swap(smart_library& lhs, smart_library& rhs) BOOST_NOEXCEPT { |
| 418 | lhs.swap(rhs); |
| 419 | } |
| 420 | |
| 421 | |
| 422 | #ifdef BOOST_DLL_DOXYGEN |
| 423 | /** Helper functions for overloads. |
| 424 | * |
| 425 | * Gets either a variable, function or member-function, depending on the signature. |
| 426 | * |
| 427 | * @code |
| 428 | * smart_library sm("lib.so"); |
| 429 | * get<int>(sm, "space::value"); //import a variable |
| 430 | * get<void(int)>(sm, "space::func"); //import a function |
| 431 | * get<some_class, void(int)>(sm, "space::class_::mem_fn"); //import a member function |
| 432 | * @endcode |
| 433 | * |
| 434 | * @param sm A reference to the @ref smart_library |
| 435 | * @param name The name of the entity to import |
| 436 | */ |
| 437 | template<class T, class T2> |
| 438 | void get(const smart_library& sm, const std::string &name); |
| 439 | #endif |
| 440 | |
| 441 | template<class T> |
| 442 | typename boost::enable_if<boost::is_object<T>, T&>::type get(const smart_library& sm, const std::string &name) |
| 443 | |
| 444 | { |
| 445 | return sm.get_variable<T>(name); |
| 446 | } |
| 447 | |
| 448 | template<class T> |
| 449 | typename boost::enable_if<boost::is_function<T>, T&>::type get(const smart_library& sm, const std::string &name) |
| 450 | { |
| 451 | return sm.get_function<T>(name); |
| 452 | } |
| 453 | |
| 454 | template<class Class, class Signature> |
| 455 | auto get(const smart_library& sm, const std::string &name) -> typename detail::get_mem_fn_type<Class, Signature>::mem_fn |
| 456 | { |
| 457 | return sm.get_mem_fn<Class, Signature>(name); |
| 458 | } |
| 459 | |
| 460 | |
| 461 | } /* namespace experimental */ |
| 462 | } /* namespace dll */ |
| 463 | } /* namespace boost */ |
| 464 | |
| 465 | #endif /* BOOST_DLL_SMART_LIBRARY_HPP_ */ |
| 466 | |