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时,忽略错误,仍然继续载入其它符号。client能够依据相应的接口是否为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
时间: 2024-08-05 00:53:07

AutoSharedLibrary -- 基于模板元编程技术的跨平台C++动态链接载入库的相关文章

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

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

【C/C++学院】0816-引用包装器/仿函数/转义字符 R”()”/using别名/模板元编程 比递归优化/智能指针/多线程/静态断言以及调试技能的要求 assert

引用包装器  std::ref(变量) #include<iostream> template<class T> void com(T arg)//模板函数,引用无效,引用包装器 { std::cout <<"com ="<< &arg << "\n"; arg++; } void main() { int count = 10; int & rcount = count; com(coun

读书笔记 effective c++ Item 48 了解模板元编程

1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在C++编译器内部运行的一个程序,它的输出——从模板中实例化出来的C++源码片段——会像往常一样被编译. 2. 使用TMP的优势 如果这没有冲击到你,是因为你没有足够尽力去想. C++不是为了模板元编程而设计的,但是自从TMP早在1990年被发现之后,它就被证明是非常有用的,为了使TMP的使用更加容易

Item 48:了解模板元编程

Item 48: Be aware of template metaprogramming. 模板元编程(Template Metaprogramming,TMP)就是利用模板来编写那些在编译时运行的C++程序. 模板元程序(Template Metaprogram)是由C++写成的,运行在编译器中的程序.当程序运行结束后,它的输出仍然会正常地编译. C++并不是为模板元编程设计的,但自90年代以来,模板元编程的用处逐渐地被世人所发现. 模板编程提供的很多便利在面向对象编程中很难实现: 程序的工

C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的Win32库去. 逻辑结构和递归说白了就是做了一个If,一个For_N,If就和Excel里的If一样,For_N是把一个模板结构迭代N遍,为了所谓的方便,把If做成了宏,写起来还挺有意思的 1 template<typename TTest, typename TTrue, typename TF

C++模板元编程 - 2 模仿haskell的列表以及相关操作

这是昨天和今天写的东西,利用C++的可变模板参数包以及包展开,模式匹配的一些东西做的,感觉用typename...比轮子叔那个List<A,List<B, List<C, D>>>的设计要好看不少. List有一个很巧妙的继承,只有那么做才能使用类似于List<>::Rest的写法,直接定义成template<typename T, typename... TArgs>List是不行的. Change这里可以给一个args...换另一个包装,这里

C++拾遗--模板元编程

C++拾遗--模板元编程 前言 模板元是用于递归加速的,把运行期的函数调用变到编译期进行代码展开,类似于内联函数.下面看一个实例:斐波那契数列第n项求解. 模板元编程 #include <iostream> #include <ctime> using namespace std; //递归法 int fib(int n) { if (n < 0) return 0; if (n == 1 || n == 2) return 1; return fib(n - 1) + fi

模板元编程

//模板元把运行时消耗的时间,在编译期间优化 //递归极其消耗时间 1 #include <iostream> 2 3 //模板元把运行时消耗的时间,在编译期间优化 4 //递归极其消耗时间 5 6 template <int N> 7 struct data 8 { 9 enum { res = data<N - 1>::res + data<N - 2>::res }; 10 }; 11 12 template <> 13 struct da

初识C++模板元编程(Template Mega Programming)

前言:毕设时在开源库上做的程序,但是源码看得很晕(当时导师告诉我这是模板元编程,可以不用太在乎),最近自己造轮子时想学习STL的源码,但也是一样的感觉,大致了解他这么做要干什么,但是不知道里面的机制.于是开始学习<C++模板元编程>,看完第二章对一些东西豁然开朗. PS:该书也有点老了,C++11标准还没出来,主要用的Boost库. Traits(特征) 说正题,在STL中经常可以见到后缀为traits的名字,比如经常用到的std::string,本质是类模板basic_string的第一个参