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 | |