基于模板元编程技术的跨平台C++动态链接加载库。通过模板技术,使用者仅需通过简单的宏,即可使编译器在编译期自动生成加载动态链接库导出符号的代码,无任何额外的运行时开销。
ASL_LIBRARY_BEGIN(TestLib) ASL_SYMBOL(Proc_test1, test1, false) ASL_SYMBOL(Proc_test2, test2, true) ASL_LIBRARY_END() TestLib theLib; try { theLib.Load("./1.so"); } catch (ASL::LibraryNotFoundException& e) { cout << e.what() << endl; // 输出./1.so } catch (ASL::SymbolNotFoundException& e) { cout << e.what() << endl; // 输出 加载失败的符号名称 } theLib.test1(1); // assert(theLib.test1 != NULL) theLib.test2("aa"); theLib.Unload();
假定有个一个名为1.so的动态链接库,并且该模块导出test1和test2两个接口。test1的接口类型为Proc_test1, test2的接口类型为Proc_test2。
ASL_SYMBOL宏的第三个参数表示,如果该符号加载失败(模块并没有导出该接口),是否抛出SymbolNotFoundException。 为false时,抛出异常,终止链接库加载流程,并且e.what()为加载失败的符号名称。为true时,忽略错误,仍然继续加载其他符号,客户端可以根据对应的接口是否为NULL来判断该符号是否加载成功。
/******************************************************************** created: 2014/05/31 file base: AutoSharedLibrary file ext: h author: qiuhan (hanzz2007@hotmail.com) purpose: Cross platform classes and macros to make dynamic link module easy to use by using c++ template meta programming technic. No need to make any changes to existing module code. Support both windows(*.dll) and linux(*.so) platforms (wchar_t & char). SPECIAL THANKS TO TRL (Template Relection Library) usage: Following codes are all in client side: ASL_LIBRARY_BEGIN(ClassName) ASL_SYMBOL(Proc_func1, func1, true) ASL_SYMBOL(Proc_func2, func2, false) ASL_LIBRARY_END() ClassName theLib; try { theLib.Load("./1.so"); } catch (ASL::LibraryNotFoundException& e) { } catch (ASL::SymbolNotFoundException& e) { } theLib.func1(1); theLib.func2("aa"); theLib.Unload(); *********************************************************************/ #ifndef ASL_INCLUDE_H #define ASL_INCLUDE_H #ifdef WIN32 #include <windows.h> #else #include <dlfcn.h> #endif #include <cstdlib> #include <exception> #include <string> namespace ASL { namespace Private { template <class Head_, class Tail_> struct TypeList { typedef Head_ Head; typedef Tail_ Tail; }; class NullType {}; template <int i_> struct Int2Type { enum { value = i_ }; }; template <int condition_, class T0_, class T1_> struct Select { typedef T0_ Result; }; template <class T0_, class T1_> struct Select<false, T0_, T1_> { typedef T1_ Result; }; template <int condition_, int v0_, int v1_> struct SelectInt { enum { value = v0_ }; }; template <int v0_, int v1_> struct SelectInt<false, v0_, v1_> { enum { value = v1_ }; }; template <class Type_, int Ignore_> struct MemberInfo { typedef Type_ Type; enum { ignore = Ignore_ }; }; template <class TList_, int startLine_, int endLine_, class ConcreteClass_> struct CreateMemberIndicesImpl { typedef typename ConcreteClass_::template IsMemberPresent<endLine_> IsMemberPresent; enum { isMemberPresent = IsMemberPresent::value }; typedef typename Select< isMemberPresent , TypeList<MemberInfo<Int2Type<IsMemberPresent::index>, IsMemberPresent::ignoreError >, TList_> , TList_ >::Result NewTList; typedef CreateMemberIndicesImpl<NewTList, startLine_, endLine_ - 1, ConcreteClass_> MemberIndicesImpl; typedef typename MemberIndicesImpl::Indices Indices; }; template <class TList_, int startLine_, class ConcreteClass_> struct CreateMemberIndicesImpl<TList_, startLine_, startLine_, ConcreteClass_> { typedef TList_ Indices; }; template <int startLine_, int endLine_, class ConcreteClass_> struct CreateMemberIndices { typedef CreateMemberIndicesImpl< NullType, startLine_ , endLine_ - 1, ConcreteClass_ > MemberIndicesImpl; typedef typename MemberIndicesImpl::Indices Indices; }; template <class ConcreteClass_, int startLine_, int currentLine_> struct GetMemberIndex { typedef typename ConcreteClass_::template IsMemberPresent<currentLine_> IsMemberPresent; enum { index = SelectInt< IsMemberPresent::value , IsMemberPresent::index , GetMemberIndex<ConcreteClass_, startLine_, currentLine_ - 1>::index >::value + 1 }; }; template <class ConcreteClass_, int startLine_> struct GetMemberIndex<ConcreteClass_, startLine_, startLine_> { enum { index = -1 }; }; } class DefaultLibraryLoader { public: typedef void* LibHandle; DefaultLibraryLoader() { lib_handle = NULL; } template<class Char_> bool Load(const Char_* name) { #if defined(WIN32) lib_handle = ::LoadLibrary(name); #else lib_handle = dlopen(name, RTLD_LAZY); #endif return lib_handle != NULL; } void Unload() { if (!lib_handle) { return; } #if defined(WIN32) ::FreeLibrary((HMODULE)lib_handle); #elif !defined(_ANDROID) dlclose(lib_handle); #endif lib_handle = NULL; } template<class Char_> void* LoadSymbol(const Char_* fun_name) { #if defined(WIN32) return (void *)::GetProcAddress((HMODULE)lib_handle, fun_name); #elif !defined(_ANDROID) return dlsym(lib_handle, fun_name); #endif } private: LibHandle lib_handle; }; class LibraryNotFoundException : public std::exception { public: LibraryNotFoundException(const char* err) { _err = err; } LibraryNotFoundException(const wchar_t* err) { static const size_t CONVERT_LEN = 256; char mbsBuff[CONVERT_LEN + 1] = { 0 }; std::wcstombs(mbsBuff, err, CONVERT_LEN); _err = mbsBuff; } ~LibraryNotFoundException() throw() {} virtual const char* what() const throw() { return _err.c_str(); } private: std::string _err; }; class SymbolNotFoundException : public std::exception { public: SymbolNotFoundException(const char* err) { _err = err; } SymbolNotFoundException(const wchar_t* err) { static const size_t CONVERT_LEN = 256; char mbsBuff[CONVERT_LEN + 1] = { 0 }; std::wcstombs(mbsBuff, err, CONVERT_LEN); _err = mbsBuff; } ~SymbolNotFoundException() throw() { } virtual const char* what() const throw() { return _err.c_str(); } std::string _err; }; struct DefaultErrorHandler { template<class Char_> static void OnLoadLibrary(const Char_* libName) { throw LibraryNotFoundException(libName); } template<class Char_> static void OnLoadSymbol(const Char_* symbolName, const bool ignore) { if (!ignore) { throw SymbolNotFoundException(symbolName); } } }; template < class ConcreteClass_, class Loader_ = DefaultLibraryLoader, class ErrorHandler_ = DefaultErrorHandler > class AutoSharedLibrary { public: AutoSharedLibrary() { } ~AutoSharedLibrary() { Unload(); } template<class Char_> void Load(ConcreteClass_& object, const Char_* p) { if (!_loader.Load(p)) { ErrorHandler_::OnLoadLibrary(p); } typedef typename ConcreteClass_::MemberIndices Indices; LoadSymbols(object, Indices()); } void Unload() { _loader.Unload(); } private: template <class Indices_> void LoadSymbols(ConcreteClass_& object, Indices_ indices) { typedef typename Indices_::Head SymInfo; typedef typename SymInfo::Type Index; bool ret = LoadSymbol(object.getLoadName(Index()), object.*ConcreteClass_::getMemberPtr(Index())); if (!ret) { ErrorHandler_::OnLoadSymbol(object.getLoadName(Index()), (bool)SymInfo::ignore); } LoadSymbols(object, typename Indices_::Tail()); } void LoadSymbols(ConcreteClass_& object, Private::NullType indices) { } template <class FuncType_, class Char_> bool LoadSymbol(const Char_* funcName, FuncType_& func) { func = (FuncType_)_loader.LoadSymbol(funcName); return func != NULL; } Loader_ _loader; }; } #define ASL_LIBRARY_BEGIN(ConcreteClass_) ASL_LIBRARY_BEGIN_2(ConcreteClass_, ASL::DefaultLibraryLoader, ASL::DefaultErrorHandler) #define ASL_LIBRARY_BEGIN_2(ConcreteClass_, LibraryLoader_, ErrorHandler_) class ConcreteClass_ { private: typedef ConcreteClass_ ConcreteClass; enum { startLine = __LINE__ }; ASL::AutoSharedLibrary<ConcreteClass_, LibraryLoader_, ErrorHandler_> _libLoader; public: ConcreteClass_() { } ~ConcreteClass_() { Unload(); } template<class Char_> void Load(const Char_* p) { _libLoader.Load(*this, p); } void Unload() { _libLoader.Unload(); } template <int lineNb_, class Dummy_ = ASL::Private::NullType> struct IsMemberPresent { enum { value = false }; enum { index = 0 }; enum { ignoreError = false }; }; #define ASL_SYMBOL_2(DataType, name, loadName, ignoreNotFound) public: DataType name; private: typedef DataType ConcreteClass::* MemberPtr##name; public: template <class Dummy_> struct IsMemberPresent<__LINE__, Dummy_> { enum { value = true }; enum { index = ASL::Private::GetMemberIndex< ConcreteClass, startLine, __LINE__ - 1>::index }; enum { ignoreError = ignoreNotFound}; }; static const char* getLoadName( ASL::Private::Int2Type<IsMemberPresent<__LINE__>::index >) { return #loadName; } static MemberPtr##name getMemberPtr( ASL::Private::Int2Type< IsMemberPresent<__LINE__>::index >) { return &ConcreteClass::name; } #define ASL_SYMBOL(DataType, name, ignoreNotFound) ASL_SYMBOL_2(DataType, name, name, ignoreNotFound) #define ASL_LIBRARY_END() private: enum { endLine = __LINE__ }; public: typedef ASL::Private::CreateMemberIndices<startLine, endLine, ConcreteClass> ::Indices MemberIndices; }; #endif