小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

移植最新版libmemcached到VC++的艱苦歷程和經驗總結(下)

 牛人的尾巴 2015-06-15
移植最新版libmemcached到VC++的艱苦歷程和經驗總結(下)
2011-10-27      0 個評論      
收藏    我要投稿

 

結果如何呢?我的VC++測試用例還是不能調用該接口的接口方法,只是這次的報錯方式有所改變,提示是每個C/C++程序員最不愿意看到的“內存地址訪問違規(guī)”,這一次我確實被郁悶了,這是為什么呢?

五、gccVC++對象模型的差異分析:

  在VC++中,C++對象(含有虛函數(shù))在編譯后將生成屬于自己的對象模型,虛擬表vtable和虛擬指針vptr均被包含在該模型中(關于該問題,可以參考Stanley Lippman的《深度探索C++對象模型》)。而我們目前的設計方式恰恰是充分利用了vptr和vtable來定位每個接口函數(shù),不幸的是,VC++生成的C++對象的vptr在該對象模型的最開始處,即該對象模型的前4個字節(jié),而gcc則不是這樣存儲vptr的。當我們通過vptr定位vtable,再通過vtable的slot定位接口函數(shù)時,也將無法得到我們期望的接口函數(shù)的入口地址,因此調用結果可想而知,而且還給人一種關公戰(zhàn)秦瓊的感覺。

六、傳統(tǒng)的設計思路和技巧:

  一條路已經走到了死胡同,唯一的辦法就是掉過頭來重新來過。這次我想到的設計思路非常簡單,也更加傳統(tǒng)。通過之前的失敗經驗總結,盡管通過VC調用gcc的純虛接口是不能正常工作的,然而gcc導出的那兩個C函數(shù)卻是可以正常調用的,同時也得到了正確的調用結果。鑒于此,我將設計一個只是包含封裝函數(shù)(封裝libmemcached的導出函數(shù))的動態(tài)庫,見如下代碼:

 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 }

  通過封裝函數(shù)的簽名可以看出,所有和libmemcached相關的結構體指針在此均被定義為void*類型,這樣就可以規(guī)避上一篇中提到的結構體由不同編譯器生成的字節(jié)對齊問題。最后,在封裝函數(shù)的內部只需要將void*轉換回其封裝的libmemcached導出函數(shù)參數(shù)期望的結構體指針類型即可,實現(xiàn)代碼如下:

  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 {
 43     return memcached_set((memcached_st*)p,key,klength,data,dlength,0,0);
 44 }
 45
 46 int MCWRAPPER_CALL wrapped_memcached_add(void* p,const char *key,size_t klength
 47         ,const char* data,size_t dlength)
 48 {
 49     return memcached_add((memcached_st*)p,key,klength,data,dlength,0,0);
 50 }
 51
 52 int MCWRAPPER_CALL wrapped_memcached_replace(void* p,const char* key,size_t klength
 53         ,const char* data,size_t dlength)
 54 {
 55     return memcached_replace((memcached_st*)p,key,klength,data,dlength,0,0);
 56 }
 57
 58 int MCWRAPPER_CALL wrapped_memcached_append(void* p,const char* key,size_t klength
 59         ,const char* data,size_t dlength)
 60 {
 61     return memcached_append((memcached_st*)p,key,klength,data,dlength,0,0);
 62 }
 63
 64 char* MCWRAPPER_CALL wrapped_memcached_get(void* p,const char* key,size_t klength
 65         ,size_t* dlength,uint32* flags,int* error)
 66 {
 67     return memcached_get((memcached_st*)p,key,klength,dlength,flags,(memcached_return_t*)error);
 68 }
 69
 70 int MCWRAPPER_CALL wrapped_memcached_mget(void* p,const char* const* keys
 71         ,const size_t* keysLength,size_t numberOfkeys)
 72 {
 73     return memcached_mget((memcached_st*)p,keys,keysLength, numberOfkeys);
 74
 75 }
 76
 77 void* MCWRAPPER_CALL wrapped_memcached_fetch_result(void* p,void* result,int* error)
 78 {
 79     return (void*)memcached_fetch_result((memcached_st*)p,NULL,(memcached_return_t*)error);
 80 }
 81
 82 const char* MCWRAPPER_CALL wrapped_memcached_result_value(const void* self)
 83 {
 84     return memcached_result_value((const memcached_result_st*)self);
 85 }
 86
 87 size_t MCWRAPPER_CALL wrapped_memcached_result_length(const void* self)
 88 {
 89     return memcached_result_length((const memcached_result_st*)self);
 90 }
 91
 92 int MCWRAPPER_CALL wrapped_memcached_delete(void* p,const char* key,size_t klength)
 93 {
 94     return memcached_delete((memcached_st*)p,key,klength,0);
 95 }
 96
 97 int MCWRAPPER_CALL wrapped_memcached_exist(void* p,const char *key,size_t klength)
 98 {
 99     return memcached_exist((memcached_st*)p,key,klength);
100 }
101
102 int MCWRAPPER_CALL wrapped_memcached_flush(void* p)
103 {
104     return memcached_flush((memcached_st*)p,0);
105 }
106
107 int MCWRAPPER_CALL wrapped_memcached_cas(void* p,const char* key,size_t klength
108         ,const char* data,size_t dlength,uint64 cas)
109 {
110     return memcached_cas((memcached_st*)p,key,klength,data,dlength,0,0,cas);
111 }
112
113 int MCWRAPPER_CALL wrapped_memcached_increment(void* p,const char* key
114         ,size_t klength,uint32 step,uint64* value)
115 {
116     return memcached_increment((memcached_st*)p,key,klength,step,value);
117 }
118
119 int MCWRAPPER_CALL wrapped_memcached_decrement(void* p,const char* key
120         ,size_t klength,uint32 step,uint64* value)
121 {
122     return memcached_decrement((memcached_st*)p,key,klength,step,value);
123 }
124
125 int MCWRAPPER_CALL wrapped_memcached_increment_with_initial(void* p
126         ,const char* key,size_t klength,uint64 step,uint64 initial,uint64* value)
127 {
128     return memcached_increment_with_initial((memcached_st*)p,key,klength,step,initial,0,value);
129 }
130
131 void MCWRAPPER_CALL wrapped_memcached_result_free(void* result)
132 {
133     memcached_result_free((memcached_result_st*)result);
134 }
135
136 int MCWRAPPER_CALL wrapped_memcached_decrement_with_initial(void* p
137         ,const char* key,size_t klength,uint64 step,uint64 initial,uint64* value)
138 {
139     return memcached_decrement_with_initial((memcached_st*)p,key,klength,step,initial,0,value);
140 }

  通過gcc在Mingw32的環(huán)境下編譯該代碼文件,并生成相應的動態(tài)庫文件(MemcachedFunctionsWrapper.dll),需要說明的是該動態(tài)庫將靜態(tài)依賴之前生成的libmemcached-8.dll,這一點可以在Mingw32下通過ldd命令予以驗證。

  剩下需要做的是去除之前聲明的純虛接口,直接導出一個C++的封裝實現(xiàn)類,在該類的實現(xiàn)中,同樣利用Windows API中的LoadLibrary和GetProcAddress方法動態(tài)加載之前生成的libmemcached函數(shù)封裝動態(tài)庫(MemcachedFunctionsWrapper.dll)。該C++類的類聲明和上一篇中實現(xiàn)類的聲明完全一致,這里就不在重復給出了。測試用例的代碼也基本相同,只是不需要再通過C接口函數(shù)獲取該C++類的對象指針了,而是可以直接將該C++類的頭文件包含進測試用例所在的工程,目前之所以這樣做是為了測試方便,一旦順利通過測試用例后,可以再考慮將該C++類放到一個獨立的VC工程中,并生成相應的dll文件,以便該模塊可以被更多其他的VC程序調用,從而提高了程序整體的復用性。

  在執(zhí)行測試用例之前,這次的心情不再像上一次那樣興奮,只是默默的等待測試結果的正確返回。然而此時,在控制臺窗口突然打印出一條libmemcached中的錯誤提示信息,看到該信息后,立刻切換到VMWire虛擬機中運行的Linux,查看memcached守護進程打印出的結果。出人意料的是,memcached沒有任何反應,再結合libmemcached剛剛輸出的錯誤信息,使我馬上意識到測試用例并沒有驅動libmemcached正常的工作,甚至根本就沒有連接到Linux中的memcached服務器。想到這里,我首先關閉了Linux中iptables的防火墻,然后在我的Windows主機中通過telnet的方式,直接登錄memcached服務器監(jiān)聽的端口,最后再切換回Linux查看memcached服務器的反應,這次memcached輸出了一條客戶端連接的信息,基于此可以證明主機(Windows)和VMWire虛擬機中的Linux之間的Socket通訊是正常的。帶著忐忑的心情,再次執(zhí)行了我的測試用例,果不其然,libmemcached輸出了同樣的錯誤信息,Linux端的memcached服務器也同樣是沒有任何反應。

  這時已經是深夜了,人的大腦也進入了一種麻木的狀態(tài),于是決定上床休息,因為之前的經驗告訴我,在這個時候離開電腦冷靜的思考往往會分析到問題的本質,并做出正確的判斷。

七、最后的決策:

  這一次我的選擇是暫時放棄移植libmemcached到VC的想法,原因如下:

  1. libmemcached版本更新過快,從0.52到0.53的發(fā)布僅僅時隔兩周,而且每兩個版本之間的代碼差異也非常大,甚至代碼的目錄組織結構也是如此,這在我移植0.49和0.53時,體現(xiàn)得非常充分。

  2. 在移植過程中,為了保證順利通過編譯,每個版本都需要進行多處修改,而且每個版本修改的地方也不一樣,有意思的是,和0.53相比,0.49需要修改的地方相對較少,移植過程也更加容易。

  3. 修改的方式五花八門,最簡單的就是gcc和VC在C++語法支持上的細節(jié)差異,這個相對簡單,相信每一個有代碼平臺遷移經驗的人都會遇到這樣的問題,再有就是沖突問題,比如libmemcached在一個枚舉中包含TRUE、FALSE、ERROR、FLOAT和SOCKET這樣的枚舉成員,不幸的是,它們與windef.h中定義的宏和typedef沖突了,因此我不得不將枚舉中的TRUE改為TRUE1,以此類推。在修改中最讓我擔心的是需要直接注釋掉一些Windows中不包含的頭文件,而這些文件在Mingw32中也沒有提供。

  4. 調試相對困難,事實上在這次遷移0.53的最后,我通過在libmemcached相應的函數(shù)中添加printf函數(shù),輸出調試信息,最終定位并修復了Socket連接的問題,但是整個過程非常繁瑣,因為libmemcached是通過gcc編譯的,因此無法在VC的工程中進行調試。當然,基于VC的測試用例在gdb下調試libmemcached也是可以的,對于習慣使用IDE的人來說,通過命令行方式調試第三方類庫,其難度可想而知。

  5. 說了這么多,最終的結果只有一個,Linux下繼續(xù)享用libmemcached和memcached服務器給我們帶來的成就感,也感謝他們的開發(fā)者無私的奉獻。至于libmemcached for Windows?希望他的作者Mrs Brian Aker能夠在未來的版本中予以足夠的關注,也期望libmemcached 1.0版本在發(fā)布時能夠提供VC++的工程文件。

八、結束語:

  希望這兩篇文章不僅僅是讓您了解了更多關于libmemcached的細節(jié),授人以魚,不如授人以漁,更希望的是與您分享我在基于不同平臺遷移C/C++代碼的經驗。如果您有更好的方法或技巧,歡迎指正,讓我們來共同提高。總之,分享是快樂的。

 

作者 Stephen_Liu

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多