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 (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
AutoSharedLibrary -- 基于模板元编程技术的跨平台C++动态链接载入库的更多相关文章
- c++ 模板元编程的一点体会
趁着国庆长假快速翻了一遍传说中的.大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身...论语言方面的深度,我看过的 c++ 书里大概只有 insi ...
- 读书笔记_Effective_C++_条款四十八:了解模板元编程
作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...
- effective c++ Item 48 了解模板元编程
1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在 ...
- 读书笔记 effective c++ Item 48 了解模板元编程
1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在 ...
- C++模板元编程(C++ template metaprogramming)
实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...
- C++模板元编程 - 函数重载决议选择工具(不知道起什么好名)完成
这个还是基于之前实现的那个MultiState,为了实现三种类型“大类”的函数重载决议:所有整数.所有浮点数.字符串,分别将这三种“大类”的数据分配到对应的Converter上. 为此实现了一些方便的 ...
- C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE
本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...
- 简单的说一下:tarits技法就是一种模板元编程,起可以将本来处于运行期的事拉到编译期来做,增加了运行效率。 看以非模板元编程的例子,就是前面的那个例子:
void adance(std::list<int>::iterator& iter, int d) { if(typeid(std::iterator_traits<std ...
- 现代c++与模板元编程
最近在重温<c++程序设计新思维>这本经典著作,感慨颇多.由于成书较早,书中很多元编程的例子使用c++98实现的.而如今c++20即将带着concept,Ranges等新特性一同到来,不得 ...
随机推荐
- Python 40 数据库-外键约束 、多对一与多对多的处理
mysql提供了 foreign key,专门用于为表和表之间 建立物理关联 思考 表里存储的是一条条的记录,两个表之间能产生的关系有哪些? 现有 A B两张表 1.多对一 2.一对一 ...
- DCOM 找不到 office word 的解决方法
1. 在运行里面 输入 comexp.msc -32 2.在“DCOM配置”中,为IIS账号配置操作Word(其他Office对象也一样)的权限. 具体操作:“组件服务(Component ...
- tp3.2 复合查询or
tp3.2 复合查询or $where['goods_name'] = array("like","%$q%");$where['goods_sn'] = ar ...
- A - Next Round
Problem description "Contestant who earns a score equal to or greater than the k-th place finis ...
- 编写linux 命令行实用工具 shell命令
今天我想以带着问题的方法学习新的技术. 问题1: 编写一个命令 语法: command options path expressions
- SQLServer XXX IS NOT NULL
SQLServer中不能使用像Java和C#中的 XXX!=null 这种用法, 使用上面的用法返回值永远是False False False 正确的用法应该是--> XXXis not ...
- ThinkPHP5中的助手函数
load_trait:快速导入Traits,PHP5.5以上无需调用 /** * 快速导入Traits PHP5.5以上无需调用 * @param string $class trait库 * ...
- 基于CXF搭建webService
1.导入相关jar包,具体哪些包我记不太清了 2.在applicationContext中加入相关配置信息,如下所示: <beans xmlns="http://www.springf ...
- 【oracle开发】wmsys.wm_concat介绍
wmsys.wm_concat是一个聚合函数,其作用是将一列数据转换成一行,也就是我们常用的行专列,但是该函数是一个undocument函数,所以不推荐大家使用这个函数.因为在后续的版本中还提不提供这 ...
- C#关闭退出线程的几种方法
.Application.Exit(); //强制所有消息中止,退出所有的窗体,但是若有托管线程(非主线程),也无法干净地退出: .System.Environment.Exit(); //无论在主线 ...