import_class.hpp 20 KB

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