基于模板元编程技术的跨平台C++动态链接加载库。通过模板技术,使用者仅需通过简单的宏,即可使编译器在编译期自动生成加载动态链接库导出符号的代码,无任何额外的运行时开销。
extern "C" { typedef int(*Proc_fnTestDll)(); typedef const char* (*Proc_fnTestDll2)(const char*); } ASL_LIBRARY_BEGIN(Test) // 强制加载名为fnTestDll的接口,如果没有该接口,则抛SymbolNotFound异常 ASL_SYMBOL_EXPLICIT(Proc_fnTestDll, fnTestDll) // 加载名为<span style="font-family: Arial, Helvetica, sans-serif;">fnTestDll2的接口,如果没有该接口,则为NULL</span> ASL_SYMBOL_OPTIONAL(Proc_fnTestDll2, fnTestDll2) // 加载名为shouldFail的接口,如果没有该接口,则为NULL</span> ASL_SYMBOL_OPTIONAL(Proc_fnTestDll2, shouldFail) // non-exists // 加载名为testFunc的接口,接口函数的类型由调用时的实参类型决定 ASL_SYMBOL_EXPLICIT_T(testFunc) // Enabled only when ' #define ASL_USE_CPP11 1 ' and compliler supports c++ 11 ASL_LIBRARY_END() int _tmain(int argc, _TCHAR* argv[]) { using namespace std; Test test; try { test.Load(_T("testDll.dll")); } catch (const ASL::LibraryNotFoundException& e) { cout << "Lib not found " << e.what() << endl; } catch (const ASL::SymbolNotFoundException& e) { cout << "Sym not found " << e.what() << endl; } assert(test.shouldFail == NULL); cout << test.fnTestDll() << endl; cout << test.fnTestDll2("hELLO, WORLD") << endl; // testFunc函数的签名由此处的实参类型推导出来,int为其返回值类型, // 这种调用方式并不安全,慎用! cout << test.testFunc<int>(ASL_ARGS_T((int)1, (int)2.f)) << endl;
test.Unload();
getchar();
return 0;
}
ASL_SYMBOL宏的第三个参数表示,如果该符号加载失败(模块并没有导出该接口),是否抛出SymbolNotFoundException。 为false时,抛出异常,终止链接库加载流程,并且e.what()为加载失败的符号名称。为true时,忽略错误,仍然继续加载其他符号,客户端可以根据对应的接口是否为NULL来判断该符号是否加载成功。
/******************************************************************** created: 2014/05/31 file base: AutoSharedLibrary file ext: h author: qiuhan ([email protected]) purpose: Cross platform classes and macros to make dynamic loaded 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_OPTIONAL(Func1Type, func1) ASL_SYMBOL_EXPLICIT(Func2Type, func2) // Enabled only when ' #define ASL_USE_CPP11 1 ' and compliler supports c++ 11 ASL_SYMBOL_EXPLICIT_T(func4) // only need to declare the name ASL_LIBRARY_END() ClassName theLib; try { theLib.Load("./1.so"); } catch (LibraryNotFoundException& e) { } catch (SymbolNotFoundException& e) { } theLib.func1(1); theLib.func2("aa"); // The function type is deduced with the args // retType => int, args => const char* AND float // So this calling is UNSAFE! // You'd better explicitly specifiy the type of args like this theLib.func4<int>(ASL_ARGS_T((const char*)"test", (float)2.3)); 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> #if ASL_USE_CPP11 #include <functional> #include <tuple> #endif 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 }; }; #if ASL_USE_CPP11 typedef void* FuncType; // Pack of numbers. // Nice idea, found at http://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer template<int ...> struct Seq {}; // Metaprogramming Expansion template<int N, int ...S> struct GenList : GenList < N - 1, N - 1, S... > {}; template<int ...S> struct GenList < 0, S... > { typedef Seq<S...> Result; }; // Function that performs the actual call template<typename Ret_, int ...S_, typename...Args_> Ret_ ActualCall(Seq<S_...>, std::tuple<Args_...> tpl, const std::function<Ret_(Args_...)>& func) { // It calls the function while expanding the std::tuple to it's arguments via std::get<S> return func(std::get<S_>(tpl) ...); } #endif } 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 (!IsLoaded()) { 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 } bool IsLoaded() const { return lib_handle != NULL; } 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; #if _MSC_VER #pragma warning(push) #pragma warning(disable: 4996) #endif char mbsBuff[CONVERT_LEN + 1] = { 0 }; std::wcstombs(mbsBuff, err, CONVERT_LEN); _err = mbsBuff; #if _MSC_VER #pragma warning(pop) #endif } ~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; #if _MSC_VER #pragma warning(push) #pragma warning(disable: 4996) #endif char mbsBuff[CONVERT_LEN + 1] = { 0 }; std::wcstombs(mbsBuff, err, CONVERT_LEN); _err = mbsBuff; #if _MSC_VER #pragma warning(pop) #endif } ~SymbolNotFoundException() throw() { } virtual const char* what() const throw() { return _err.c_str(); } private: 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(ConcreteClass_::getLoadName(Index()), object.*ConcreteClass_::getMemberPtr(Index())); if (!ret) { ErrorHandler_::OnLoadSymbol(ConcreteClass_::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(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; } #if ASL_USE_CPP11 #define ASL_SYMBOL_T(name, loadName, ignoreNotFound) \ ASL_SYMBOL(ASL::Private::FuncType, name##_private_, loadName, ignoreNotFound) \ template<class Ret_, class... Args_> Ret_ name (std::tuple<Args_...> args) \ { \ typedef Ret_(*FuncPointer)(Args_...); \ std::function<Ret_(Args_...)> func = reinterpret_cast<FuncPointer>(name##_private_); \ return ASL::Private::ActualCall(typename ASL::Private::GenList<sizeof...(Args_)>::Result(), args, func); \ } #define ASL_SYMBOL_EXPLICIT_T(name) \ ASL_SYMBOL_T(name, name, false) #define ASL_ARGS_T(...) (std::make_tuple<>(__VA_ARGS__)) #endif #define ASL_SYMBOL_DEFAULT(DataType, name, ignoreNotFound) \ ASL_SYMBOL(DataType, name, name, ignoreNotFound) #define ASL_SYMBOL_OPTIONAL(DataType, name) \ ASL_SYMBOL_DEFAULT(DataType, name, true) #define ASL_SYMBOL_EXPLICIT(DataType, name) \ ASL_SYMBOL_DEFAULT(DataType, name, false) #define ASL_LIBRARY_END() \ private: \ enum { endLine = __LINE__ }; \ public: \ typedef ASL::Private::CreateMemberIndices<startLine, endLine, ConcreteClass> \ ::Indices MemberIndices; \ }; #endif