1 //  Copyright 2016 Klemens Morgenstern
2 //  Copyright 2019-2020 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_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      */
shared_lib() const82     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     */
symbol_storage() const90     const mangled_storage &symbol_storage() const {return _storage;}
91 
92     ///Overload, for current development.
symbol_storage()93     mangled_storage &symbol_storage() {return _storage;}
94 
95     //! \copydoc shared_library::shared_library()
smart_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)
smart_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(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)
smart_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)
smart_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      */
smart_library(const smart_library & lib)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     */
smart_library(BOOST_RV_REF (smart_library)lib)130     smart_library(BOOST_RV_REF(smart_library) lib) BOOST_NOEXCEPT
131         : _lib(boost::move(lib._lib)), _storage(boost::move(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       */
smart_library(const shared_library & lib)141       explicit smart_library(const shared_library & lib) BOOST_NOEXCEPT
142           : _lib(lib)
143       {
144           _storage.load(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      */
smart_library(BOOST_RV_REF (shared_library)lib)153      explicit smart_library(BOOST_RV_REF(shared_library) lib) BOOST_NOEXCEPT
154          : _lib(boost::move(static_cast<shared_library&>(lib)))
155      {
156          _storage.load(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     */
~smart_library()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)
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(lib_path);
173         _lib.load(lib_path, mode, ec);
174 
175         if (ec) {
176             boost::dll::detail::report_error(ec, "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)
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(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)
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(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>
get_variable(const std::string & name) const208     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>
get_function(const std::string & name) const234     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>
get_mem_fn(const std::string & name) const263     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>
get_constructor() const286     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>
get_destructor() const308     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>
get_type_info() const329     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      */
add_type_alias(const std::string & name)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()
unload()359     void unload() BOOST_NOEXCEPT {
360         _storage.clear();
361         _lib.unload();
362     }
363 
364     //! \copydoc shared_library::is_loaded() const
is_loaded() const365     bool is_loaded() const BOOST_NOEXCEPT {
366         return _lib.is_loaded();
367     }
368 
369     //! \copydoc shared_library::operator!() const
operator !() const370     bool operator!() const BOOST_NOEXCEPT {
371         return !is_loaded();
372     }
373 
374     //! \copydoc shared_library::operator bool() const
BOOST_EXPLICIT_OPERATOR_BOOL()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
has(const std::string & symbol_name) const383     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)
assign(const smart_library & lib)388     smart_library& assign(const smart_library& lib) {
389        _lib.assign(lib._lib);
390        _storage.assign(lib._storage);
391        return *this;
392     }
393 
394     //! \copydoc shared_library::swap(shared_library& rhs)
swap(smart_library & rhs)395     void swap(smart_library& rhs) BOOST_NOEXCEPT {
396         _lib.swap(rhs._lib);
397         _storage.swap(rhs._storage);
398     }
399 };
400 
401 /// Very fast equality check that compares the actual DLL/DSO objects. Throws nothing.
operator ==(const smart_library & lhs,const smart_library & rhs)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.
operator !=(const smart_library & lhs,const smart_library & rhs)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.
operator <(const smart_library & lhs,const smart_library & rhs)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.
swap(smart_library & lhs,smart_library & rhs)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>
get(const smart_library & sm,const std::string & name)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>
get(const smart_library & sm,const std::string & name)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>
get(const smart_library & sm,const std::string & name)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