当前位置:编程学习 > VC++ >>

移植最新版libmemcached到VC++的艰苦历程和经验总结(上)

 

零、前言:

  该篇博客的Title原计划是“在VC++中调用libmemcached的设计技巧”,可结果却事与原违,原因很简单,移植失败了。尽管结果如此,然而这3天的付出却是非常值得的,原因也很简单,收获非常大。事实上,我曾经在6月份的时候成功移植了当时的最新版本0.49,并写出了下面的博客:http://www.zzzyk.com/kf/201110/109203.html

  这次移植的目标非常明确,就是基于上次的经验,对libmemcached进行基于C++的封装,以便其可以更好的集成到我的底层服务框架中,使我的程序在Windows平台也可以享受memcached服务器带来的性能优势。带着这份憧憬开始了我的艰苦移植历程。首先需要说明的是0.49和最新版0.53之间的差异是非常大的,这一点也让我始料不及,因此走了一些弯路,好在及时做出调整,才没有耽搁更多的时间去验证一些不可能的事情。下面是我在动手移植libmemcached之前的设计思路,移植过程中遇到的问题,以及移植失败后的经验总结。

一、最初的设计思路:

  为什么不在VC中直接调用编译后的libmemcached库呢?原因非常简单,我们无法直接调用。由于目标库(libmemcached)是在Mingw32环境下通过gcc编译的,而gcc在Windows下编译动态链接库时(DLL)并没有生成相应的lib文件,这样在VC中也就无法通过静态链接动态库的方式将libmemcached链接到调用程序中。这样我们只能利用Windows中提供的另一种方式,即通过LoadLibrary和GetProcAddress等Win32 API来动态加载该动态库,因为该方法不需要.lib文件。尽管该方式在技术上被认为是可能行的,然而libmemcached中存在大量的导出函数,以及这些函数所依赖的结构体(struct)。为了保证GetProcAddress返回的函数指针能够正常的被调用,如memcached_create等,我们不得不在当前工程中定义(typedef)大量的函数指针以及相关的结构体。鉴于之前的移植经验,由于这些结构体嵌套了大量的内部子结构体,因此如果全部定义就需要大量的工作。而这些结构体的成员很多都是用于libmemcached内部,因此一旦在未来的版本中修改了该结构体的成员,那么我们的程序也不得不要随之改变,可以想象,这样的同步是相当痛苦的。除此之外,还有一个非常致命的缺陷,即结构体成员的字节对齐问题。如果不是一字节对齐,如VC缺省的8字节对齐,那么gcc和VC编译器在处理该类问题时就可能存在一定的差异,一旦如此,VC中定义的结构体填充的数据就不能被gcc正确的取出,从而导致libmemcached中函数无易做图常的工作。

  为了避免以上问题的发生,下面我就来介绍一种通用的设计技巧用于解决该类问题。

  1. 定义一个C++的纯虚接口和两个C的导出函数,其中C++的纯虚接口用于之后的程序调用,该接口中定义了部分libmemcached中提供的功能,如add、set、replace、get、delete和CAS等。然而需要注意的是该接口中并未包含或暴露任何和libmemcached相关的信息,如memcached_st结构体、memcached_create函数等。见如下代码:

 1 class MemcachedClientWrapper

 2 {

 3 protected:

 4     virtual ~MemcachedClientWrapper() {}

 5

 6 public:

 7     virtual bool initialize(bool consistentHash = false, bool supportCAS = false) = 0;

 8     virtual int addServer(const char* serverIP, const int port) = 0;

 9     virtual void release() = 0;

10     virtual bool set(void* key,int klength,void* data,int dlength) = 0;

11     virtual bool add(void* key,int klength,void* data,int dlength) = 0;

12     virtual bool replace(void* key,int klength,void* data,int dlength) = 0;

13     virtual bool append(void* key,int klength,void* data,int dlength) = 0;

14     virtual bool get(void* key,int klength,MCFetchedData*& fetchedData) = 0;

15     virtual bool gets(void** keys,int* keysLength,int count) = 0;

16     virtual bool fetchNext(MCFetchedData*& fetchedData,bool* isEof = 0) = 0;

17     virtual bool remove(void* key,int klength) = 0;

18     virtual bool exists(void* key,int klength,bool* ok = 0) = 0;

19     virtual bool updateWithCAS(void* key,int klength,void* data,int dlength) = 0;

20     virtual bool updateWithAtomicIncrement(void* key,int klength,int step

21             ,uint64& value,int* defaultValue = 0) = 0;

22     virtual bool updateWithAtomicDecrement(void* key,int klength,int step

23             ,uint64& value,int* defaultValue = 0) = 0;

24     virtual bool clean() = 0;

25 };

  两个导出的C函数主要用于创建该接口的实现子类,以及在使用完毕后释放该接口的指针,从而保证资源释放的可靠性,见如下代码:

1 #define WRAPPER_CC __attribute__((cdcel))

2

3 extern "C" {

4     //工厂方法创建Wrapper的实现子类,但是返回接口的指针

5     MemcachedClientWrapper* WRAPPER_CC createMCWrapper();

6     //资源释放函数,通过上面函数返回的接口指针,需要通过该函数释放

7     void WRAPPER_CC releaseMCWrapper(MemcachedClientWrapper*);

8 }

     这里之所以使用cdecl的调用规范,而不是Windows API常用的stdcall,主要是因为gcc在编译基于stdcall调用规范的C导出函数时,在导出函数名的后面添加了一个@和该函数参数所占的字节数作为后缀,因此当我们通过GetProcAddress传入函数名获取函数指针时,由于名称不匹配,所以返回的函数指针将为NULL。和stdcall不同的是,基于cdcel调用规范生成的导出函数不存在这样的问题。我们可以通过Windows提供的Dependency工具予以证明。

  2. 定义一个实现类继承自上面定义的纯虚接口,该类将包含大量和libmemcached相关的细节信息,同时也需要在该类中include和libmemcached相关的头文件,而不是再自行重新定义和libmemcached相关的细节信息,最后再通过动态加载的方式进行加载,见如下代码声明:

 1 #include <MemcachedClientWrapper.h>

 2 #include <libmemcached/memcached.h>

 3

 4 class MemcachedClientWrapperImpl : public MemcachedClientWrapper

 5 {

 6 public:

 7     MemcachedClientWrapperImpl();

 8     virtual ~MemcachedClientWrapperImpl();

 9

10 public:

11     virtual bool initialize(bool consistentHash = false, bool supportCAS = false);

12     void release();

13     int addServer(const char* serverIP, const int port);

14     bool set(void* key,int klength,void* data,int dlength);

15     bool add(void* key,int klength,void* data,int dlength);

16     bool replace(void* key,int klength,void* data,int dlength);

17     bool append(void* key,int klength,void* data,int dlength);

18     bool get(void* key,int klength,MCFetchedData*& fetchedData);

19     bool gets(void** keys,int* keysLength,int count);

20     bool fetchNext(MCFetchedData*& fetchedData,bool* isEof = 0);

21     bool remove(void* key,int klength);

22     bool exists(void* key,int klength,bool* ok = NULL);

23     bool updateWithCAS(void* key,int klength,void* data,int dlength);

24     bool updateWithAtomicIncrement(void* key,int klength,int step,uint64& value,int* defaultValue = 0);

25     bool updateWithAtomicDecrement(void* key,int klength,int step,uint64& value,int* defaultValue = 0);

26     bool clean();

27

28 private:

29     memcached_st*    _mc;

30     memcached_return _mr;

31   &nbs

补充:软件开发 , Vc ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,