移植最新版libmemcached到VC++的艰苦历程和经验总结(下)
结果如何呢?我的VC++测试用例还是不能调用该接口的接口方法,只是这次的报错方式有所改变,提示是每个C/C++程序员最不愿意看到的“内存地址访问违规”,这一次我确实被郁闷了,这是为什么呢?
五、gcc和VC++对象模型的差异分析:
在VC++中,C++对象(含有虚函数)在编译后将生成属于自己的对象模型,虚拟表vtable和虚拟指针vptr均被包含在该模型中(关于该问题,可以参考Stanley Lippman的《深度探索C++对象模型》)。而我们目前的设计方式恰恰是充分利用了vptr和vtable来定位每个接口函数,不幸的是,VC++生成的C++对象的vptr在该对象模型的最开始处,即该对象模型的前4个字节,而gcc则不是这样存储vptr的。当我们通过vptr定位vtable,再通过vtable的slot定位接口函数时,也将无法得到我们期望的接口函数的入口地址,因此调用结果可想而知,而且还给人一种关公战秦琼的感觉。
六、传统的设计思路和技巧:
一条路已经走到了死胡同,唯一的办法就是掉过头来重新来过。这次我想到的设计思路非常简单,也更加传统。通过之前的失败经验总结,尽管通过VC调用gcc的纯虚接口是不能正常工作的,然而gcc导出的那两个C函数却是可以正常调用的,同时也得到了正确的调用结果。鉴于此,我将设计一个只是包含封装函数(封装libmemcached的导出函数)的动态库,见如下代码:
1#define MCWRAPPER_CALL __attribute__((cdecl))
2
3 extern "C" {
4 void* MCWRAPPER_CALL wrapped_memcached_create();
5 void MCWRAPPER_CALL wrapped_memcached_free(void* p);
6 void MCWRAPPER_CALL wrapped_memcached_result_free(void* result);
7 const char* MCWRAPPER_CALL wrapped_memcached_strerror(void* p, int error);
8 int MCWRAPPER_CALL wrapped_memcached_behavior_set(void *p, const int flag, uint64 data);
9 int MCWRAPPER_CALL wrapped_memcached_behavior_set_distribution(void* p, int type);
10 int MCWRAPPER_CALL wrapped_memcached_server_add(void* p,const char* hostname, uint16 port);
11 uint32 MCWRAPPER_CALL wrapped_memcached_server_count(const void* p);
12 int MCWRAPPER_CALL wrapped_memcached_set(void* p,const char* key
13 ,size_t klength,const char* data,size_t dlength);
14 int MCWRAPPER_CALL wrapped_memcached_add(void* p,const char *key
15 ,size_t klength,const char* data,size_t dlength);
16 int MCWRAPPER_CALL wrapped_memcached_replace(void* p,const char* key
17 ,size_t klength,const char* data,size_t dlength);
18 int MCWRAPPER_CALL wrapped_memcached_append(void* p,const char* key
19 ,size_t klength,const char* data,size_t dlength);
20 char* MCWRAPPER_CALL wrapped_memcached_get(void* p,const char* key,size_t klength
21 ,size_t* dlength,uint32* flags,int* error);
22 int MCWRAPPER_CALL wrapped_memcached_mget(void* p,const char* const* keys
23 ,const size_t* keysLength,size_t numberOfkeys);
24 void* MCWRAPPER_CALL wrapped_memcached_fetch_result(void* p,void* result,int* error);
25 const char* MCWRAPPER_CALL wrapped_memcached_result_value(const void* self);
26 size_t MCWRAPPER_CALL wrapped_memcached_result_length(const void* self);
27 int MCWRAPPER_CALL wrapped_memcached_delete(void* p,const char* key,size_t klength);
28 int MCWRAPPER_CALL wrapped_memcached_exist(void* p,const char *key,size_t klength);
29 int MCWRAPPER_CALL wrapped_memcached_flush(void* p);
30 int MCWRAPPER_CALL wrapped_memcached_cas(void* p,const char* key,size_t klength
31 ,const char* data,size_t dlength,uint64 cas);
32 int MCWRAPPER_CALL wrapped_memcached_increment(void* p,const char* key
33 ,size_t klength,uint32 step,uint64* value);
34 int MCWRAPPER_CALL wrapped_memcached_decrement(void* p,const char* key
35 ,size_t klength,uint32 step,uint64* value);
36 int MCWRAPPER_CALL wrapped_memcached_increment_with_initial(void* p
37 ,const char* key,size_t klength,uint64 step,uint64 initial,uint64* value);
38 int MCWRAPPER_CALL wrapped_memcached_decrement_with_initial(void* p
39 ,const char* key,size_t klength,uint64 step,uint64 initial,uint64* value);
40 }
通过封装函数的签名可以看出,所有和libmemcached相关的结构体指针在此均被定义为void*类型,这样就可以规避上一篇中提到的结构体由不同编译器生成的字节对齐问题。最后,在封装函数的内部只需要将void*转换回其封装的libmemcached导出函数参数期望的结构体指针类型即可,实现代码如下:
1#include <MemcachedFunctionsWrapper.h>
2 #include <libmemcached/memcached.h>
3
4 void* MCWRAPPER_CALL wrapped_memcached_create()
5 {
6 return (void*)memcached_create(NULL);
7 }
8
9 void MCWRAPPER_CALL wrapped_memcached_free(void* p)
10 {
11 memcached_free((memcached_st*)p);
12 }
13
14 const char* MCWRAPPER_CALL wrapped_memcached_strerror(void* p, int error)
15 {
16 return memcached_strerror((memcached_st*)p,(memcached_return)error);
17 }
18
19 int MCWRAPPER_CALL wrapped_memcached_behavior_set(void *p, const int flag, uint64 data)
20 {
21 return memcached_behavior_set((memcached_st*)p,(memcached_behavior_t)flag,data);
22 }
23
24 int MCWRAPPER_CALL wrapped_memcached_behavior_set_distribution(void* p, int type)
25 {
26 return memcached_behavior_set_distribution((memcached_st*)p
27 ,(memcached_server_distribution_t)type);
28 }
29
30 int MCWRAPPER_CALL wrapped_memcached_server_add(void* p,const char* hostname, uint16 port)
31 {
32 return memcached_server_add((memcached_st*)p,hostname,port);
33 }
34
35 uint32 MCWRAPPER_CALL wrapped_memcached_server_count(const void* p)
36 {
37 return memcached_server_count((memcached_st*)p);
38 }
39
40 int MCWRAPPER_CALL wrapped_memcached_set(void* p,const char* key,size_t klength
41 ,const char* data,size_t dlength)
42 {
补充:软件开发 , Vc ,