// Copyright 2015-2018 Klemens D. Morgenstern // Copyright 2019-2021 Antony Polukhin // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_DLL_IMPORT_CLASS_HPP_ #define BOOST_DLL_IMPORT_CLASS_HPP_ /// \file boost/dll/import_class.hpp /// \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 /// \brief Contains the boost::dll::experimental::import_class function for importing classes. #include #include #include #if (__cplusplus < 201103L) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201103L) # error This file requires C++11 at least! #endif #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif namespace boost { namespace dll { namespace experimental { namespace detail { template struct deleter { destructor dtor; bool use_deleting; deleter(const destructor & dtor, bool use_deleting = false) : dtor(dtor), use_deleting(use_deleting) {} void operator()(T*t) { if (use_deleting) dtor.call_deleting(t); else { dtor.call_standard(t); //the thing is actually an array, so delete[] auto p = reinterpret_cast(t); delete [] p; } } }; template struct mem_fn_call_proxy; template struct mem_fn_call_proxy> { typedef boost::dll::experimental::detail::mangled_library_mem_fn mem_fn_t; Class* t; mem_fn_t & mem_fn; mem_fn_call_proxy(mem_fn_call_proxy&&) = default; mem_fn_call_proxy(const mem_fn_call_proxy & ) = delete; mem_fn_call_proxy(Class * t, mem_fn_t & mem_fn) : t(t), mem_fn(mem_fn) {} template auto operator()(Args&&...args) const { return mem_fn(t, std::forward(args)...); } }; template struct mem_fn_call_proxy { T* t; const std::string &name; smart_library &_lib; mem_fn_call_proxy(mem_fn_call_proxy&&) = default; mem_fn_call_proxy(const mem_fn_call_proxy&) = delete; mem_fn_call_proxy(T *t, const std::string &name, smart_library & _lib) : t(t), name(name), _lib(_lib) {}; Return operator()(Args...args) const { auto f = _lib.get_mem_fn(name); return (t->*f)(static_cast(args)...); } }; } template class imported_class; template imported_class import_class(const smart_library& lib, Args...args); template imported_class import_class(const smart_library& lib, const std::string & alias_name, Args...args); template imported_class import_class(const smart_library& lib, std::size_t size, Args...args); template imported_class import_class(const smart_library& lib, std::size_t size, const std::string & alias_name, Args...args); /*! This class represents an imported class. * * \note It must be constructed via \ref boost::dll::import_class(const smart_library& lib, std::size_t, Args...) * * \tparam The type or type-alias of the imported class. */ template class imported_class { smart_library _lib; std::unique_ptr> _data; bool _is_allocating; std::size_t _size; const std::type_info& _ti; template inline std::unique_ptr> make_data(const smart_library& lib, Args ... args); template inline std::unique_ptr> make_data(const smart_library& lib, std::size_t size, Args...args); template imported_class(detail::sequence *, const smart_library& lib, Args...args); template imported_class(detail::sequence *, const smart_library& lib, std::size_t size, Args...args); template imported_class(detail::sequence *, smart_library&& lib, Args...args); template imported_class(detail::sequence *, smart_library&& lib, std::size_t size, Args...args); public: //alias to construct with explicit parameter list template static imported_class make(smart_library&& lib, Args...args) { typedef detail::sequence *seq; return imported_class(seq(), boost::move(lib), static_cast(args)...); } template static imported_class make(smart_library&& lib, std::size_t size, Args...args) { typedef detail::sequence *seq; return imported_class(seq(), boost::move(lib), size, static_cast(args)...); } template static imported_class make(const smart_library& lib, Args...args) { typedef detail::sequence *seq; return imported_class(seq(), lib, static_cast(args)...); } template static imported_class make(const smart_library& lib, std::size_t size, Args...args) { typedef detail::sequence *seq; return imported_class(seq(), lib, size, static_cast(args)...); } typedef imported_class base_t; ///Returns a pointer to the underlying class T* get() {return _data.get();} imported_class() = delete; imported_class(imported_class&) = delete; imported_class(imported_class&&) = default; /// ().empty();} ///Check if the imported class is move-assignable bool is_move_assignable() {return !_lib.symbol_storage().template get_mem_fn ("operator=").empty();} ///Check if the imported class is copy-constructible bool is_copy_constructible() {return !_lib.symbol_storage().template get_constructor().empty();} ///Check if the imported class is copy-assignable bool is_copy_assignable() {return !_lib.symbol_storage().template get_mem_fn("operator=").empty();} imported_class copy() const; /// move(); /// & lhs) const; ///Invoke the move assignment. \attention Undefined behaviour if the imported object is not move assignable. void move_assign( imported_class & lhs); ///Check if the class is loaded. explicit operator bool() const {return _data;} ///Get a const reference to the std::type_info. const std::type_info& get_type_info() {return _ti;}; /*! Call a member function. This returns a proxy to the function. * The proxy mechanic mechanic is necessary, so the signaute can be passed. * * \b Example * * \code * im_class.call("function_name")("MyString"); * \endcode */ template const detail::mem_fn_call_proxy call(const std::string& name) { return detail::mem_fn_call_proxy(_data.get(), name, _lib); } /*! Call a qualified member function, i.e. const and or volatile. * * \b Example * * \code * im_class.call("function_name")("MyString"); * \endcode */ template>> const detail::mem_fn_call_proxy call(const std::string& name) { return detail::mem_fn_call_proxy(_data.get(), name, _lib); } ///Overload of ->* for an imported method. template const detail::mem_fn_call_proxy> operator->*(detail::mangled_library_mem_fn& mn) { return detail::mem_fn_call_proxy>(_data.get(), mn); } ///Import a method of the class. template typename boost::dll::experimental::detail::mangled_import_type>::type import(const std::string & name) { return boost::dll::experimental::import_mangled(_lib, name); } }; //helper function, uses the allocating template template inline std::unique_ptr> imported_class::make_data(const smart_library& lib, Args ... args) { constructor ctor = lib.get_constructor(); destructor dtor = lib.get_destructor (); if (!ctor.has_allocating() || !dtor.has_deleting()) { boost::dll::fs::error_code ec; ec = boost::dll::fs::make_error_code( boost::dll::fs::errc::bad_file_descriptor ); // report_error() calls dlsym, do not use it here! boost::throw_exception( boost::dll::fs::system_error( ec, "boost::dll::detail::make_data() failed: no allocating ctor or dtor was found" ) ); } return std::unique_ptr> ( ctor.call_allocating(static_cast(args)...), detail::deleter(dtor, false /* not deleting dtor*/)); } //helper function, using the standard template template inline std::unique_ptr> imported_class::make_data(const smart_library& lib, std::size_t size, Args...args) { constructor ctor = lib.get_constructor(); destructor dtor = lib.get_destructor (); if (!ctor.has_standard() || !dtor.has_standard()) { boost::dll::fs::error_code ec; ec = boost::dll::fs::make_error_code( boost::dll::fs::errc::bad_file_descriptor ); // report_error() calls dlsym, do not use it here! boost::throw_exception( boost::dll::fs::system_error( ec, "boost::dll::detail::make_data() failed: no regular ctor or dtor was found" ) ); } T *data = reinterpret_cast(new char[size]); ctor.call_standard(data, static_cast(args)...); return std::unique_ptr> ( reinterpret_cast(data), detail::deleter(dtor, false /* not deleting dtor*/)); } template template imported_class::imported_class(detail::sequence *, const smart_library & lib, Args...args) : _lib(lib), _data(make_data(lib, static_cast(args)...)), _is_allocating(false), _size(0), _ti(lib.get_type_info()) { } template template imported_class::imported_class(detail::sequence *, const smart_library & lib, std::size_t size, Args...args) : _lib(lib), _data(make_data(lib, size, static_cast(args)...)), _is_allocating(true), _size(size), _ti(lib.get_type_info()) { } template template imported_class::imported_class(detail::sequence *, smart_library && lib, Args...args) : _lib(boost::move(lib)), _data(make_data(lib, static_cast(args)...)), _is_allocating(false), _size(0), _ti(lib.get_type_info()) { } template template imported_class::imported_class(detail::sequence *, smart_library && lib, std::size_t size, Args...args) : _lib(boost::move(lib)), _data(make_data(lib, size, static_cast(args)...)), _is_allocating(true), _size(size), _ti(lib.get_type_info()) { } template inline imported_class boost::dll::experimental::imported_class::copy() const { if (this->_is_allocating) return imported_class::template make(_lib, *_data); else return imported_class::template make(_lib, _size, *_data); } template inline imported_class boost::dll::experimental::imported_class::move() { if (this->_is_allocating) return imported_class::template make(_lib, *_data); else return imported_class::template make(_lib, _size, *_data); } template inline void boost::dll::experimental::imported_class::copy_assign(const imported_class& lhs) const { this->call("operator=")(*lhs._data); } template inline void boost::dll::experimental::imported_class::move_assign(imported_class& lhs) { this->call("operator=")(static_cast(*lhs._data)); } /*! * Returns an instance of \ref imported_class which allows to call or import more functions. * It takes a copy of the smart_libray, so no added type_aliases will be visible, * for the object. * * Few compilers do implement an allocating constructor, which allows the construction * of the class without knowing the size. That is not portable, so the actual size of the class * shall always be provided. * * \b Example: * * \code * auto import_class(lib, "class_name", 20, "param1", 42); * \endcode * * In this example we construct an instance of the class "class_name" with the size 20, which has "type_alias" as an alias, * through a constructor which takes a const-ref of std::string and an std::size_t parameter. * * \tparam T Class type or alias * \tparam Args Constructor argument list. * \param lib Path to shared library or shared library to load function from. * \param name Null-terminated C or C++ mangled name of the function to import. Can handle std::string, char*, const char*. * \param mode An mode that will be used on library load. * * \return class object. * * \throw \forcedlinkfs{system_error} if symbol does not exist or if the DLL/DSO was not loaded. * Overload that accepts path also throws std::bad_alloc in case of insufficient memory. */ template imported_class import_class(const smart_library& lib_, std::size_t size, Args...args) { smart_library lib(lib_); return imported_class::template make(boost::move(lib), size, static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(const smart_library& lib_, Args...args) { smart_library lib(lib_); return imported_class::template make(boost::move(lib), static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(const smart_library& lib_, const std::string & alias_name, Args...args) { smart_library lib(lib_); lib.add_type_alias(alias_name); return imported_class::template make(boost::move(lib), static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(const smart_library& lib_, std::size_t size, const std::string & alias_name, Args...args) { smart_library lib(lib_); lib.add_type_alias(alias_name); return imported_class::template make(boost::move(lib), size, static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(const smart_library& lib_, const std::string & alias_name, std::size_t size, Args...args) { smart_library lib(lib_); lib.add_type_alias(alias_name); return imported_class::template make(boost::move(lib), size, static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(smart_library && lib, Args...args) { return imported_class::template make(boost::move(lib), static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(smart_library && lib, const std::string & alias_name, Args...args) { lib.add_type_alias(alias_name); return imported_class::template make(boost::move(lib), static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(smart_library && lib, std::size_t size, Args...args) { return imported_class::template make(boost::move(lib), size, static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(smart_library && lib, std::size_t size, const std::string & alias_name, Args...args) { lib.add_type_alias(alias_name); return imported_class::template make(boost::move(lib), size, static_cast(args)...); } //! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) template imported_class import_class(smart_library && lib, const std::string & alias_name, std::size_t size, Args...args) { lib.add_type_alias(alias_name); return imported_class::template make(boost::move(lib), size, static_cast(args)...); } /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library. */ template imported_class import_class(smart_library & lib, Args...args) { return imported_class::template make(lib, static_cast(args)...); } /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library. */ template imported_class import_class(smart_library & lib, const std::string & alias_name, Args...args) { lib.add_type_alias(alias_name); return imported_class::template make(lib, static_cast(args)...); } /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library. */ template imported_class import_class(smart_library & lib, std::size_t size, Args...args) { return imported_class::template make(lib, size, static_cast(args)...); } /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library. */ template imported_class import_class(smart_library & lib, std::size_t size, const std::string & alias_name, Args...args) { lib.add_type_alias(alias_name); return imported_class::template make(lib, size, static_cast(args)...); } /*! \overload boost::dll::import_class(const smart_library& lib, std::size_t, Args...) * \note This function does add the type alias to the \ref boost::dll::experimental::smart_library. */ template imported_class import_class(smart_library & lib, const std::string & alias_name, std::size_t size, Args...args) { lib.add_type_alias(alias_name); return imported_class::template make(lib, size, static_cast(args)...); } } } } #endif /* BOOST_DLL_IMPORT_CLASS_HPP_ */