设为首页 加入收藏

TOP

移植最新版libmemcached到VC++的艰苦历程和经验总结(上) (一)
2014-11-23 21:30:28 】 浏览:1547
Tags:移植 最新版 libmemcached 艰苦 历程 经验 总结

零、前言:

  该篇博客的Title原计划是“在VC++中调用libmemcached的设计技巧”,可结果却事与原违,原因很简单,移植失败了。尽管结果如此,然而这3天的付出却是非常值得的,原因也很简单,收获非常大。事实上,我曾经在6月份的时候成功移植了当时的最新版本0.49,并写出了下面的博客:http://www.2cto.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导出函数时,在导出函数名的后面添加了一个@和该函数参数所占的字

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇VC#数据库的连接 下一篇VC++ 2005编译链接错误汇总

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目