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