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