import_class.hpp 20 KB

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