1 // Copyright 2015-2018 Klemens D. 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_IMPORT_CLASS_HPP_
8 #define BOOST_DLL_IMPORT_CLASS_HPP_
9 
10 #include <boost/dll/smart_library.hpp>
11 #include <boost/dll/import_mangled.hpp>
12 #include <memory>
13 
14 #ifdef BOOST_HAS_PRAGMA_ONCE
15 # pragma once
16 #endif
17 
18 namespace boost { namespace dll { namespace experimental {
19 
20 namespace detail
21 {
22 
23 template<typename T>
24 struct deleter
25 {
26     destructor<T> dtor;
27     bool use_deleting;
28 
deleterboost::dll::experimental::detail::deleter29     deleter(const destructor<T> & dtor, bool use_deleting = false) :
30         dtor(dtor), use_deleting(use_deleting) {}
31 
operator ()boost::dll::experimental::detail::deleter32     void operator()(T*t)
33     {
34         if (use_deleting)
35             dtor.call_deleting(t);
36         else
37         {
38             dtor.call_standard(t);
39             //the thing is actually an array, so delete[]
40             auto p = reinterpret_cast<char*>(t);
41             delete [] p;
42         }
43     }
44 };
45 
46 template<class T, class = void>
47 struct mem_fn_call_proxy;
48 
49 template<class Class, class U>
50 struct mem_fn_call_proxy<Class, boost::dll::experimental::detail::mangled_library_mem_fn<Class, U>>
51 {
52     typedef boost::dll::experimental::detail::mangled_library_mem_fn<Class, U> mem_fn_t;
53     Class* t;
54     mem_fn_t & mem_fn;
55 
56     mem_fn_call_proxy(mem_fn_call_proxy&&) = default;
57     mem_fn_call_proxy(const mem_fn_call_proxy & ) = delete;
mem_fn_call_proxyboost::dll::experimental::detail::mem_fn_call_proxy58     mem_fn_call_proxy(Class * t, mem_fn_t & mem_fn)
59                 : t(t), mem_fn(mem_fn) {}
60 
61     template<typename ...Args>
operator ()boost::dll::experimental::detail::mem_fn_call_proxy62     auto operator()(Args&&...args) const
63     {
64         return mem_fn(t, std::forward<Args>(args)...);
65     }
66 
67 };
68 
69 template<class T, class Return, class ...Args>
70 struct mem_fn_call_proxy<T, Return(Args...)>
71 {
72     T* t;
73     const std::string &name;
74     smart_library &_lib;
75 
76     mem_fn_call_proxy(mem_fn_call_proxy&&) = default;
77     mem_fn_call_proxy(const mem_fn_call_proxy&) = delete;
mem_fn_call_proxyboost::dll::experimental::detail::mem_fn_call_proxy78     mem_fn_call_proxy(T *t, const std::string &name, smart_library & _lib)
79         : t(t), name(name), _lib(_lib) {};
80 
operator ()boost::dll::experimental::detail::mem_fn_call_proxy81     Return operator()(Args...args) const
82     {
83         auto f = _lib.get_mem_fn<T, Return(Args...)>(name);
84         return (t->*f)(static_cast<Args>(args)...);
85     }
86 };
87 
88 }
89 
90 template<typename T>
91 class imported_class;
92 
93 template<typename T, typename ... Args> imported_class<T>
94 import_class(const smart_library& lib, Args...args);
95 template<typename T, typename ... Args> imported_class<T>
96 import_class(const smart_library& lib, const std::string & alias_name, Args...args);
97 template<typename T, typename ... Args> imported_class<T>
98 import_class(const smart_library& lib, std::size_t size, Args...args);
99 template<typename T, typename ... Args> imported_class<T>
100 import_class(const smart_library& lib, std::size_t size,
101              const std::string & alias_name, Args...args);
102 
103 
104 /*! This class represents an imported class.
105  *
106  * \note It must be constructed via \ref boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
107  *
108  * \tparam The type or type-alias of the imported class.
109  */
110 template<typename T>
111 class imported_class
112 {
113     smart_library _lib;
114     std::unique_ptr<T, detail::deleter<T>> _data;
115     bool _is_allocating;
116     std::size_t _size;
117     const std::type_info& _ti;
118 
119     template<typename ... Args>
120     inline std::unique_ptr<T, detail::deleter<T>> make_data(const smart_library& lib, Args ... args);
121     template<typename ... Args>
122     inline std::unique_ptr<T, detail::deleter<T>> make_data(const smart_library& lib, std::size_t size, Args...args);
123 
124     template<typename ...Args>
125     imported_class(detail::sequence<Args...> *, const smart_library& lib,  Args...args);
126 
127     template<typename ...Args>
128     imported_class(detail::sequence<Args...> *, const smart_library& lib, std::size_t size,  Args...args);
129 
130     template<typename ...Args>
131     imported_class(detail::sequence<Args...> *, smart_library&& lib,  Args...args);
132 
133     template<typename ...Args>
134     imported_class(detail::sequence<Args...> *, smart_library&& lib, std::size_t size,  Args...args);
135 public:
136     //alias to construct with explicit parameter list
137     template<typename ...Args>
make(smart_library && lib,Args...args)138     static imported_class<T> make(smart_library&& lib,  Args...args)
139     {
140         typedef detail::sequence<Args...> *seq;
141         return imported_class(seq(), boost::move(lib), static_cast<Args>(args)...);
142     }
143 
144     template<typename ...Args>
make(smart_library && lib,std::size_t size,Args...args)145     static imported_class<T> make(smart_library&& lib, std::size_t size,  Args...args)
146     {
147         typedef detail::sequence<Args...> *seq;
148         return imported_class(seq(), boost::move(lib), size, static_cast<Args>(args)...);
149     }
150     template<typename ...Args>
make(const smart_library & lib,Args...args)151     static imported_class<T> make(const smart_library& lib,  Args...args)
152     {
153         typedef detail::sequence<Args...> *seq;
154         return imported_class(seq(), lib, static_cast<Args>(args)...);
155     }
156 
157     template<typename ...Args>
make(const smart_library & lib,std::size_t size,Args...args)158     static imported_class<T> make(const smart_library& lib, std::size_t size,  Args...args)
159     {
160         typedef detail::sequence<Args...> *seq;
161         return imported_class(seq(), lib, size, static_cast<Args>(args)...);
162     }
163 
164     typedef imported_class<T> base_t;
165     ///Returns a pointer to the underlying class
get()166     T* get() {return _data.get();}
167     imported_class() = delete;
168 
169     imported_class(imported_class&) = delete;
170     imported_class(imported_class&&) = default;                ///<Move constructor
171     imported_class& operator=(imported_class&) = delete;
172     imported_class& operator=(imported_class&&) = default;  ///<Move assignmend
173 
174     ///Check if the imported class is move-constructible
is_move_constructible()175     bool is_move_constructible() {return !_lib.symbol_storage().template get_constructor<T(T&&)>     ().empty();}
176     ///Check if the imported class is move-assignable
is_move_assignable()177     bool is_move_assignable()    {return !_lib.symbol_storage().template get_mem_fn<T, T&(T&&)>     ("operator=").empty();}
178     ///Check if the imported class is copy-constructible
is_copy_constructible()179     bool is_copy_constructible() {return !_lib.symbol_storage().template get_constructor<T(const T&)>().empty();}
180     ///Check if the imported class is copy-assignable
is_copy_assignable()181     bool is_copy_assignable()    {return !_lib.symbol_storage().template get_mem_fn<T, T&(const T&)>("operator=").empty();}
182 
183     imported_class<T> copy() const; ///<Invoke the copy constructor. \attention Undefined behaviour if the imported object is not copy constructible.
184     imported_class<T> move();       ///<Invoke the move constructor. \attention Undefined behaviour if the imported object is not move constructible.
185 
186     ///Invoke the copy assignment. \attention Undefined behaviour if the imported object is not copy assignable.
187     void copy_assign(const imported_class<T> & lhs) const;
188     ///Invoke the move assignment. \attention Undefined behaviour if the imported object is not move assignable.
189     void move_assign(      imported_class<T> & lhs);
190 
191     ///Check if the class is loaded.
operator bool() const192     explicit operator bool() const  {return _data;}
193 
194     ///Get a const reference to the std::type_info.
get_type_info()195     const std::type_info& get_type_info() {return _ti;};
196 
197     /*! Call a member function. This returns a proxy to the function.
198      * The proxy mechanic mechanic is necessary, so the signaute can be passed.
199      *
200      * \b Example
201      *
202      * \code
203      * im_class.call<void(const char*)>("function_name")("MyString");
204      * \endcode
205      */
206     template<class Signature>
call(const std::string & name)207     const detail::mem_fn_call_proxy<T, Signature> call(const std::string& name)
208     {
209         return detail::mem_fn_call_proxy<T, Signature>(_data.get(), name, _lib);
210     }
211     /*! Call a qualified member function, i.e. const and or volatile.
212      *
213      * \b Example
214      *
215      * \code
216      * im_class.call<const type_alias, void(const char*)>("function_name")("MyString");
217      * \endcode
218      */
219     template<class Tin, class Signature, class = boost::enable_if<detail::unqalified_is_same<T, Tin>>>
call(const std::string & name)220     const detail::mem_fn_call_proxy<Tin, Signature> call(const std::string& name)
221     {
222         return detail::mem_fn_call_proxy<Tin, Signature>(_data.get(), name, _lib);
223     }
224     ///Overload of ->* for an imported method.
225     template<class Tin, class T2>
226     const detail::mem_fn_call_proxy<Tin, boost::dll::experimental::detail::mangled_library_mem_fn<Tin, T2>>
operator ->*(detail::mangled_library_mem_fn<Tin,T2> & mn)227             operator->*(detail::mangled_library_mem_fn<Tin, T2>& mn)
228     {
229         return detail::mem_fn_call_proxy<Tin, boost::dll::experimental::detail::mangled_library_mem_fn<Tin, T2>>(_data.get(), mn);
230     }
231 
232     ///Import a method of the class.
233     template <class ...Args>
234     typename boost::dll::experimental::detail::mangled_import_type<boost::dll::experimental::detail::sequence<T, Args...>>::type
import(const std::string & name)235     import(const std::string & name)
236     {
237         return boost::dll::experimental::import_mangled<T, Args...>(_lib, name);
238     }
239 };
240 
241 
242 
243 //helper function, uses the allocating
244 template<typename T>
245 template<typename ... Args>
make_data(const smart_library & lib,Args...args)246 inline std::unique_ptr<T, detail::deleter<T>> imported_class<T>::make_data(const smart_library& lib, Args ... args)
247 {
248     constructor<T(Args...)> ctor = lib.get_constructor<T(Args...)>();
249     destructor<T>           dtor = lib.get_destructor <T>();
250 
251     if (!ctor.has_allocating() || !dtor.has_deleting())
252     {
253         boost::dll::fs::error_code ec;
254 
255         ec = boost::dll::fs::make_error_code(
256             boost::dll::fs::errc::bad_file_descriptor
257         );
258 
259         // report_error() calls dlsym, do not use it here!
260         boost::throw_exception(
261             boost::dll::fs::system_error(
262                 ec, "boost::dll::detail::make_data() failed: no allocating ctor or dtor was found"
263             )
264         );
265     }
266 
267      return std::unique_ptr<T, detail::deleter<T>> (
268             ctor.call_allocating(static_cast<Args>(args)...),
269             detail::deleter<T>(dtor, false /* not deleting dtor*/));
270 }
271 
272 //helper function, using the standard
273 template<typename T>
274 template<typename ... Args>
make_data(const smart_library & lib,std::size_t size,Args...args)275 inline std::unique_ptr<T, detail::deleter<T>> imported_class<T>::make_data(const smart_library& lib, std::size_t size, Args...args)
276 {
277     constructor<T(Args...)> ctor = lib.get_constructor<T(Args...)>();
278     destructor<T>           dtor = lib.get_destructor <T>();
279 
280     if (!ctor.has_standard() || !dtor.has_standard())
281     {
282         boost::dll::fs::error_code ec;
283 
284         ec = boost::dll::fs::make_error_code(
285             boost::dll::fs::errc::bad_file_descriptor
286         );
287 
288         // report_error() calls dlsym, do not use it here!
289         boost::throw_exception(
290             boost::dll::fs::system_error(
291                 ec, "boost::dll::detail::make_data() failed: no regular ctor or dtor was found"
292             )
293         );
294     }
295 
296     T *data = reinterpret_cast<T*>(new char[size]);
297 
298     ctor.call_standard(data, static_cast<Args>(args)...);
299 
300     return std::unique_ptr<T, detail::deleter<T>> (
301             reinterpret_cast<T*>(data),
302             detail::deleter<T>(dtor, false /* not deleting dtor*/));
303 
304 }
305 
306 
307 template<typename T>
308 template<typename ...Args>
imported_class(detail::sequence<Args...> *,const smart_library & lib,Args...args)309 imported_class<T>::imported_class(detail::sequence<Args...> *, const smart_library & lib,  Args...args)
310     : _lib(lib),
311       _data(make_data<Args...>(lib, static_cast<Args>(args)...)),
312       _is_allocating(false),
313       _size(0),
314       _ti(lib.get_type_info<T>())
315 {
316 
317 }
318 
319 template<typename T>
320 template<typename ...Args>
imported_class(detail::sequence<Args...> *,const smart_library & lib,std::size_t size,Args...args)321 imported_class<T>::imported_class(detail::sequence<Args...> *, const smart_library & lib, std::size_t size,  Args...args)
322     : _lib(lib),
323       _data(make_data<Args...>(lib, size, static_cast<Args>(args)...)),
324       _is_allocating(true),
325       _size(size),
326       _ti(lib.get_type_info<T>())
327 {
328 
329 }
330 
331 template<typename T>
332 template<typename ...Args>
imported_class(detail::sequence<Args...> *,smart_library && lib,Args...args)333 imported_class<T>::imported_class(detail::sequence<Args...> *, smart_library && lib,  Args...args)
334     : _lib(boost::move(lib)),
335       _data(make_data<Args...>(lib, static_cast<Args>(args)...)),
336       _is_allocating(false),
337       _size(0),
338       _ti(lib.get_type_info<T>())
339 {
340 
341 }
342 
343 template<typename T>
344 template<typename ...Args>
imported_class(detail::sequence<Args...> *,smart_library && lib,std::size_t size,Args...args)345 imported_class<T>::imported_class(detail::sequence<Args...> *, smart_library && lib, std::size_t size,  Args...args)
346     : _lib(boost::move(lib)),
347       _data(make_data<Args...>(lib, size, static_cast<Args>(args)...)),
348       _is_allocating(true),
349       _size(size),
350       _ti(lib.get_type_info<T>())
351 {
352 
353 }
354 
355 template<typename T>
copy() const356 inline imported_class<T> boost::dll::experimental::imported_class<T>::copy() const
357 {
358     if (this->_is_allocating)
359         return imported_class<T>::template make<const T&>(_lib, *_data);
360     else
361         return imported_class<T>::template make<const T&>(_lib, _size, *_data);
362 }
363 
364 template<typename T>
move()365 inline imported_class<T> boost::dll::experimental::imported_class<T>::move()
366 {
367     if (this->_is_allocating)
368         return imported_class<T>::template make<T&&>(_lib, *_data);
369     else
370         return imported_class<T>::template make<T&&>(_lib, _size, *_data);
371 }
372 
373 template<typename T>
copy_assign(const imported_class<T> & lhs) const374 inline void boost::dll::experimental::imported_class<T>::copy_assign(const imported_class<T>& lhs) const
375 {
376     this->call<T&(const T&)>("operator=")(*lhs._data);
377 }
378 
379 template<typename T>
move_assign(imported_class<T> & lhs)380 inline void boost::dll::experimental::imported_class<T>::move_assign(imported_class<T>& lhs)
381 {
382     this->call<T&(T&&)>("operator=")(static_cast<T&&>(*lhs._data));
383 }
384 
385 
386 
387 /*!
388 * Returns an instance of \ref imported_class which allows to call or import more functions.
389 * It takes a copy of the smart_libray, so no added type_aliases will be visible,
390 * for the object.
391 *
392 * Few compilers do implement an allocating constructor, which allows the construction
393 * of the class without knowing the size. That is not portable, so the actual size of the class
394 * shall always be provided.
395 *
396 * \b Example:
397 *
398 * \code
399 * auto import_class<class type_alias, const std::string&, std::size_t>(lib, "class_name", 20, "param1", 42);
400 * \endcode
401 *
402 * In this example we construct an instance of the class "class_name" with the size 20, which has "type_alias" as an alias,
403 * through a constructor which takes a const-ref of std::string and an std::size_t parameter.
404 *
405 * \tparam T Class type or alias
406 * \tparam Args Constructor argument list.
407 * \param lib Path to shared library or shared library to load function from.
408 * \param name Null-terminated C or C++ mangled name of the function to import. Can handle std::string, char*, const char*.
409 * \param mode An mode that will be used on library load.
410 *
411 * \return class object.
412 *
413 * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded.
414 *       Overload that accepts path also throws std::bad_alloc in case of insufficient memory.
415 */
416 template<typename T, typename ... Args> imported_class<T>
import_class(const smart_library & lib_,std::size_t size,Args...args)417 import_class(const smart_library& lib_, std::size_t size, Args...args)
418 {
419     smart_library lib(lib_);
420 
421     return imported_class<T>::template make<Args...>(boost::move(lib), size, static_cast<Args>(args)...);
422 }
423 
424 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
425 template<typename T, typename ... Args> imported_class<T>
import_class(const smart_library & lib_,Args...args)426 import_class(const smart_library& lib_, Args...args)
427 {
428     smart_library lib(lib_);
429     return imported_class<T>::template make<Args...>(boost::move(lib), static_cast<Args>(args)...);
430 }
431 
432 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
433 template<typename T, typename ... Args> imported_class<T>
import_class(const smart_library & lib_,const std::string & alias_name,Args...args)434 import_class(const smart_library& lib_, const std::string & alias_name, Args...args)
435 {
436     smart_library lib(lib_);
437     lib.add_type_alias<T>(alias_name);
438     return imported_class<T>::template make<Args...>(boost::move(lib), static_cast<Args>(args)...);
439 }
440 
441 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
442 template<typename T, typename ... Args> imported_class<T>
import_class(const smart_library & lib_,std::size_t size,const std::string & alias_name,Args...args)443 import_class(const smart_library& lib_, std::size_t size, const std::string & alias_name, Args...args)
444 {
445     smart_library lib(lib_);
446 
447     lib.add_type_alias<T>(alias_name);
448     return imported_class<T>::template make<Args...>(boost::move(lib), size, static_cast<Args>(args)...);
449 }
450 
451 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
452 template<typename T, typename ... Args> imported_class<T>
import_class(const smart_library & lib_,const std::string & alias_name,std::size_t size,Args...args)453 import_class(const smart_library& lib_, const std::string & alias_name, std::size_t size, Args...args)
454 {
455     smart_library lib(lib_);
456 
457     lib.add_type_alias<T>(alias_name);
458     return imported_class<T>::template make<Args...>(boost::move(lib), size, static_cast<Args>(args)...);
459 }
460 
461 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
462 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library && lib,Args...args)463 import_class(smart_library && lib, Args...args)
464 {
465     return imported_class<T>::template make<Args...>(boost::move(lib), static_cast<Args>(args)...);
466 }
467 
468 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
469 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library && lib,const std::string & alias_name,Args...args)470 import_class(smart_library && lib, const std::string & alias_name, Args...args)
471 {
472     lib.add_type_alias<T>(alias_name);
473     return imported_class<T>::template make<Args...>(boost::move(lib), static_cast<Args>(args)...);
474 }
475 
476 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
477 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library && lib,std::size_t size,Args...args)478 import_class(smart_library && lib, std::size_t size, Args...args)
479 {
480     return imported_class<T>::template make<Args...>(boost::move(lib), size, static_cast<Args>(args)...);
481 }
482 
483 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
484 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library && lib,std::size_t size,const std::string & alias_name,Args...args)485 import_class(smart_library && lib, std::size_t size, const std::string & alias_name, Args...args)
486 {
487     lib.add_type_alias<T>(alias_name);
488     return imported_class<T>::template make<Args...>(boost::move(lib), size, static_cast<Args>(args)...);
489 }
490 
491 //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
492 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library && lib,const std::string & alias_name,std::size_t size,Args...args)493 import_class(smart_library && lib, const std::string & alias_name, std::size_t size, Args...args)
494 {
495     lib.add_type_alias<T>(alias_name);
496     return imported_class<T>::template make<Args...>(boost::move(lib), size, static_cast<Args>(args)...);
497 }
498 
499 
500 
501 /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
502  * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library.
503  */
504 
505 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library & lib,Args...args)506 import_class(smart_library & lib, Args...args)
507 {
508     return imported_class<T>::template make<Args...>(lib, static_cast<Args>(args)...);
509 }
510 
511 /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
512  * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library.
513  */
514 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library & lib,const std::string & alias_name,Args...args)515 import_class(smart_library & lib, const std::string & alias_name, Args...args)
516 {
517     lib.add_type_alias<T>(alias_name);
518     return imported_class<T>::template make<Args...>(lib, static_cast<Args>(args)...);
519 }
520 
521 /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
522  * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library.
523  */
524 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library & lib,std::size_t size,Args...args)525 import_class(smart_library & lib, std::size_t size, Args...args)
526 {
527     return imported_class<T>::template make<Args...>(lib, size, static_cast<Args>(args)...);
528 }
529 
530 /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
531  * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library.
532  */
533 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library & lib,std::size_t size,const std::string & alias_name,Args...args)534 import_class(smart_library & lib, std::size_t size, const std::string & alias_name, Args...args)
535 {
536     lib.add_type_alias<T>(alias_name);
537     return imported_class<T>::template make<Args...>(lib, size, static_cast<Args>(args)...);
538 }
539 
540 /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...)
541  * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library.
542  */
543 template<typename T, typename ... Args> imported_class<T>
import_class(smart_library & lib,const std::string & alias_name,std::size_t size,Args...args)544 import_class(smart_library & lib, const std::string & alias_name, std::size_t size, Args...args)
545 {
546     lib.add_type_alias<T>(alias_name);
547     return imported_class<T>::template make<Args...>(lib, size, static_cast<Args>(args)...);
548 }
549 
550 }
551 }
552 }
553 
554 
555 
556 #endif /* BOOST_DLL_IMPORT_CLASS_HPP_ */
557