import_class.hpp 19 KB

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