基类DLL完成的事情包括:
1、实现加载和关闭动态库的函数open,close
2、查找派生类通过IMPORT宏加入的导出函数地址,并将其保存在派生类里定义的函数指针变量上
因此对于派生类的IMPORT宏,主要工作就是定义一个指定函数名称的函数指针变量,这样我们通过一个简单的FunTypeWrapper模板来定义:
template<class T>
struct FunTypeWrapper
{
typedef T type;
};
#define IMPORT_FUN(name, type) FunTypeWrapper<type>::type name;
由于基类需要从dll里根据导出函数名称来找到导出函数的地址,因此IMORT宏还需要能向基类注册对应导出函数的名称,为了简化宏处理,对每个要加载的导出函数,我们定义一个Fun类型的成员对象,并在其缺省构造函数中完成名称注册以及函数符号地址的查找和保存工作:
template<class FunType, const char *(*getSymbol)()>
struct Fun
{
Fun()
{
const char * funName = getSymbol();
if (!s_bSymbolReady)
{
DLL::s_funSymbols->push_back(funName);
}
else
{
for (int i=0; i<DLL::s_funSymbols.size(); ++i)
{
if (0 == strcmp(funName, DLL::s_funSymbols[i].first))
{
*(void**)(this-sizeof(void*)) = DLL::s_funSymbols[i].second;
break;
}
}
}
}
};
上面类中,由于缺省构造函数不带参数,因此通过模板参数传人了关键的函数名称和类型信息,而名称信息更是通过获取函数名称的函数指针的方式传入,多了这个间接层是为了同时解决在派生类定义字符串常量和通过模板参数传递这个字符串常量在语法上实现的问题,因此最后IMPORT宏的定义如下:
#define IMPORT_FUN(name, type) \
FunTypeWrapper<type>::type name; \
static const char* name##_symbol(){return name;} \
Fun<type,name##_symbol> name##_load;
上面倒数第二行定义了获取函数名称常量字符串的静态函数,最后一行则是我们用于协助加载动态库函数的Fun对象。
有了前面这些基础,剩下来的就是收尾工作了,DLL模板代码如下:
template<class Derive>
struct DLL
{
template<class T>
struct FunTypeWrapper
{
typedef T type;
};
template<class FunType, const char *(*getSymbol)()>
struct Fun
{
Fun()
{
const char * funName = getSymbol();
if (!s_bSymbolReady)
{
DLL::s_funSymbols->push_back(funName);
}
else
{
for (int i=0; i<DLL::s_funSymbols.size(); ++i)
{
if (0 == strcmp(funName, DLL::s_funSymbols[i].first))
{
*(void**)(this-sizeof(void*)) = DLL::s_funSymbols[i].second;
break;
}
}
}
}
};
private:
static Derive s_instance; //定义单例对象
static std::vector<std::pair<const char*, void*> > m_funSymbols; //中间保存派生类导出函数信息的符号表
static bool s_bSymbolReady; //符号表是否加载标记
void * m_handle; //dll句柄
public:
static Derive * instance()
{
return &s_instance;
}
bool open(const char *pcDllPath, int mode)
{
//打开dll
m_handle = dlopen(pcDllPath, mode);
if (NULL == m_handle)
{
return false;
}
//构造一个派生类对象,获取需要加载的函数符号
s_bSymbolReady = false;
Derive obj;
//加载所有的函数符号
for (size_t i=0; i<m_funSymbols.size(); ++i)
{
m_funSymbols[i].second = dlsym(m_handle, m_funSymbols[i].first);
}
//重新构造当前对象,并将符号表里的函数地址赋值给派生类的函数指针成员变量
s_bSymbolReady = true;
new (this) Derive();
//清空符号表,为下次加载使用
m_funSymbols.clear();
return true;
}
void close()
{
dlclose(m_handle);
m_handle = 0;
}
};
//相关静态参数的定义
template <class Derive>std::vector<std::pair<const char*, void*> > DLL<Derive>::m_funSymbols;
template <class Derive>Derive DLL<Derive>::s_instance;
template <class Derive>bool DLL<Derive>::s_bSymbolReady;
现在我们可以使用这个类来重新定义下:
struct Memory : DLL<Memory>
{
IMPORT_FUN(malloc, (void*(*)(size_t)));
IMPORT_FUN(free, (void(*)(void*)));
IMPORT_FUN(calloc, (void*(*)(size_t,size_t)));
};
将使用方式修改为单例方式:
Memory::instance()->open("/usr/local/lib/memory.dll", RTLD_LAZY); //动态加载dll库
void *pBuff = Memory::instance()->malloc(1024); //调用动态库的导出方法
Memory::instance()->free(pBuff);
Memory::instance()->close();
还可以使用对象方式:
Memory oMemory;
oMemory.open("/usr/local/lib/memory.dll", RTLD_LAZY); //动态加载dll库
void *pBuff = oMemory.malloc(1024); //调用动态库的导出方法
oMemory.free(pBuff);
oMemory.close();