AutoSharedLibrary -- 基于模板元编程技术的跨平台C++动态链接加载库

 基于模板元编程技术的跨平台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





你可能感兴趣的:(AutoSharedLibrary -- 基于模板元编程技术的跨平台C++动态链接加载库)