基于模板元编程技术的跨平台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 (hanzz2007@hotmail.com)
- 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