qiezi的学习园地

AS/C/C++/D/Java/JS/Python/Ruby

  C++博客 :: 首页 :: 新随笔 ::  ::  :: 管理 ::
很久以前看到有人问“如何在C++中实现动态加载类”时,简单地做了一个。

不过当时没有去考虑动态加载DLL的情况。

今天在cpp@codingnow.com中也有人问到这个问题,就把它给做完了。

当然只是简单地做到了“从全局类型库中,根据类名创建实例,支持动态DLL加载”,说得更明白点:

在应用程序App1中,向全局类型库中注册一个类型"Test",在另一个隐式链接的DLL中(即App1一启动就加载的DLL),向全局类型库中注册另外几个类型。这时可以在App1中通过类型的名字生成实例。

在另一个显式加载的DLL中(即调用LoadLibrary加载),向全局类型库中注册其它几个类型。这时通过LoadLibrary加载这个DLL,就可以生成这几个类型的实例了。

这地方不能上传文件,就把代码贴一点吧:

typelib.h文件:

#ifndef __TYPE_LIB_H__
#define __TYPE_LIB_H__

typedef 
void*(*CREATE_FUNC)();
typedef 
void(*RELEASE_FUNC)(void*);

void regtype (const char* name, CREATE_FUNC cfunc, RELEASE_FUNC rfunc);

void* createObject (const char* name);

void releaseObject (const char* name, void* p);

struct ITestInterface
{
    
virtual ~ITestInterface () {}
    
virtual void print () const = 0;
};

template 
<class T>
void* create ()
{
    
return new T;
}

template 
<class T>
void release (void* p)
{
    delete (T
*)p;
}

#endif // __TYPE_LIB_H__

typelib.cpp文件:

#include "typelib.h"

#include 
<string>
#include 
<map>
using namespace std;

namespace TypeRegistry
{
    
static map < string, pair<CREATE_FUNC, RELEASE_FUNC> >  types_info;

    template 
<class T>
    
void regType (const string& name)
    {
        types_info.insert (make_pair(name, make_pair(create
<T>, release<T>)));
    }
}

void regtype (const char* name, CREATE_FUNC cfunc, RELEASE_FUNC rfunc)
{
    TypeRegistry::types_info.insert (make_pair(name, make_pair(cfunc, rfunc)));
}

void* createObject (const char* name)
{
    map 
< string, pair<CREATE_FUNC, RELEASE_FUNC> >::const_iterator iter;
    iter 
= TypeRegistry::types_info.find (name);
    
if (iter != TypeRegistry::types_info.end ())
        
return (*iter->second.first)();
    
return NULL;
}

void releaseObject (const char* name, void* p)
{
    map 
< string, pair<CREATE_FUNC, RELEASE_FUNC> >::const_iterator iter;
    iter 
= TypeRegistry::types_info.find (name);
    
if (iter != TypeRegistry::types_info.end ())
        (
*iter->second.second)(p);
}

把它编译成静态lib或DLL,就可以使用了。

在那2个为我们提供类型的DLL中,DllMain函数中加入下面的代码:

// FirstTest和SecondTest是2个类名
regtype("FirstTest", create<FirstTest>, release<FirstTest>);
regtype(
"FirstTest", create<SecondTest>, release<SecondTest>);

就可以向全局类型库中注册类型。注意在类型库中是没有保存类信息的,所以最好是使用单根类库来做。

下面是一点测试代码:

int main()
{
       
// 程序启动时注册类型。
       
// 实际上启动时就加载了另一个动态链接库,那里面有3个类型,所以现在有4个类型
       regtype ("MyTest", create<MyTest>, release<MyTest>);
       
while (1)
       {
               
string class_name;
               cin 
>> class_name;
               
if (class_name == "q")
                       
break;
               
// 当输入load时,把另一个动态链接库加载进来,那个链接库中有2个类型,现在共有6个类型可用。
               if (class_name == "load")
               {
                       LoadLibrary(
"typelibdll_test.dll");
                       
continue;
               }
               ITestInterface
* test = (ITestInterface*)createObject (class_name.c_str());
               
if (!test)
               {
                       cout 
<< "This type not found" << endl;
                       
continue;
               }
               test
->print ();
               releaseObject (class_name.c_str(), test);
       }
       
return 0;
}

还有一个没考虑的地方,就是没有给它加锁,因为有可能在一个线程中加载一个DLL。

不过我还有些怀疑这东西是否真的有用?
posted on 2005-09-26 17:31 qiezi 阅读(675) 评论(11)  编辑 收藏 引用 所属分类: C++自家破烂

评论

# re: C++实现简单的类型库 2005-09-26 17:54 天下无双
很有意思啊.  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 18:24 可冰
哦,我以前问过这样的问题,不知道看到的是不是我问的.
不管了,反正好好学习就是了.  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 18:39 可冰
这个肯定有用了啊!
还可以,和我以前想的一样,但我还不太了解DLL的用法,所以也就没有实现.而且我总感觉这样做不是很好,但又想不到更好的.现在我就省点力借借光吧,用的时候完善一下就直接用了.
不过,我不知道在LINUX及其它系统上面是不是也有DLL机制的方式来实现同样的功能.  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 19:44 cpunion
我第一份实现不是在DllMain中实现的,使用的是一个“注册器”:

struct TypesRegister
{
TypesRegister ()
{
regtype(...);
.....
}
}

static TypesRegister __types_register__;

在每个动态库中都用这种方式来注册(可以写成宏),就可以跨平台,跨编译器了。。

这个实现是有些简陋,所以说它是“简单的类型库”也有些过大,不过大致原理也就是这么样子了,asgard项目也可以考虑这种做法。  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 19:47 cpunion
我上面所说的“asgard项目也可以考虑这种做法”,是说对于内置类型和自定义,要注册到类型库,这样可以动态创建,而且类型库可以单独更新,方便扩充内置类型。

上面说的还有一点问题,我还不知道linux下是否支持动态加载动态链接库呢。。。晚上查一下资料。  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 20:35 cpunion
linux下使用dlopen好像并不能调用全局对象的构造函数,看来这个还真有点麻烦。。。  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 21:19 cpunion
dlopen和LoadLibrary一样,也会调用全局对象的构造函数,我这里没有调用是因为在2个DLL中,注册器都是用的同一个名字,结果只能调用第一个加载的。

解决办法是把TypesRegister类修改成不同的名字。  回复  更多评论
  

# re: C++实现简单的类型库 2005-09-26 21:46 cpunion
g++编译器还可以采用另一种方式:

void start () __attribute__((constructor));

void start ()
{
regtype ("Test", create <Test>, release <Test>);
}

这个函数应该是在所有的运行时环境都初始化好以后才调用的,函数名可任意修改,只要指定constructor属性却可。对应的还有一个destructor,会在动态链接库析出内存时调用,如果是应用程序,则是在main之前和之后调用。  回复  更多评论
  

# re: C++实现简单的类型库 2006-01-23 09:40 橙子
// .h
class DynamicFnBase {
public:
DynamicFnBase(const TCHAR* dllName, const TCHAR* fnName);
~DynamicFnBase();
bool isValid() const {return fnPtr != 0;}
protected:
void* fnPtr;
HMODULE dllHandle;
private:
DynamicFnBase(const DynamicFnBase&);
DynamicFnBase operator=(const DynamicFnBase&);
};

template<class T> class DynamicFn : public DynamicFnBase {
public:
DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
T operator *() const {return (T)fnPtr;};
};

//---------------------------------------------------------------------------------
// .cpp
DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const TCHAR* fnName) : dllHandle(0), fnPtr(0) {
TCHAR outinfo[500];
dllHandle = LoadLibrary(dllName);
if (!dllHandle) {
_stprintf(outinfo,_T("DLL %s not found (%d)"), dllName, GetLastError());
MessageBox(NULL,outinfo,_T("Dll Error"),0);
return;
}
fnPtr = GetProcAddress(dllHandle, fnName);
if (!fnPtr)
{
_stprintf(outinfo,_T("proc %s not found in %s (%d)"), fnName, dllName, GetLastError());
MessageBox(NULL,outinfo,_T("Dll Error"),0);
}

}

DynamicFnBase::~DynamicFnBase() {

if (dllHandle)
FreeLibrary(dllHandle);
}

//--------------------------------------------------------------------------------------
// test
typedef void (*WM_Hooks_proto)(UINT min, UINT max);
DynamicFn<WM_Hooks_proto> WM_Hooks(_T("hook.dll"), "WM_Hooks");

//--------------------------------------------------------------------------------------

  回复  更多评论
  

# re: C++实现简单的类型库[未登录] 2008-09-02 11:17 aaaa
里面的内存泄漏怎么消除??  回复  更多评论
  

# re: C++实现简单的类型库[未登录] 2008-10-13 13:14 along
这个实现不能实现,如java式的动态加载吧!如果我给的是一个类型的字符串呢?  回复  更多评论
  


标题  
姓名  
主页
验证码 *
内容(提交失败后,可以通过“恢复上次提交”恢复刚刚提交的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
[使用Ctrl+Enter键可以直接提交]
相关链接:
网站导航: