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
36namespace boost {
37namespace dll {
38namespace experimental {
39
40using boost::dll::detail::constructor;
41using 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*/
74class smart_library {
75 shared_library _lib;
76 detail::mangled_storage_impl _storage;
77
78public:
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.
402inline 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.
407inline 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.
412inline 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.
417inline 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 */
437template<class T, class T2>
438void get(const smart_library& sm, const std::string &name);
439#endif
440
441template<class T>
442typename 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
448template<class T>
449typename 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
454template<class Class, class Signature>
455auto 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

source code of boost/libs/dll/include/boost/dll/smart_library.hpp