1. 程式人生 > >【bug】error: multiple types in one declaration

【bug】error: multiple types in one declaration

背景

我的專案採用CMake構建,專案中使用了Thrift庫,在構建程式碼的時候就遇到了一次這個問題,見下圖

我通過修改專案主目錄下的CMakefilelist.txt規避了這個問題,顯式為libevent指定一個版本號

ADD_DEFINITIONS(-DLIBEVENT_VERSION_NUMBER=0x02010800)

  清理構建過程中間資料,重新構建編譯,一切順利。

bug原因分析

StackOverflow上有人對這種一個符號多次定義的問題做了分析,參考:C++ “multiple types in one declaration” error

不過這是從個人開發角度來說的,答者的觀點是:產生這種bug的原因主要還是因為個人寫程式碼時的不小心,比如少寫個;等等。

不過作為開源庫,這種因為少些符號或者筆誤導致的問題基本上不會出現的,那麼就很有必要分析這個問題產生的根本原因了。

根據報錯位置,逐步跟蹤程式碼

到這一步問題基本上就很清楚了,THRIFT_SOCKET這個符號分別在/usr/local/include/thrift/server/TNonblockingServer.h 和 /usr/local/include/thrift/transport/PlatformSocket.h中進行了定義。PlatformSocket.h有兩處定義,這兩處是根據g++ 預定義巨集(predefined macros)來區分是否是_Win32架構。

OK,現在問題找到了。能否對這個問題更進一步思考呢? 我的專案中很顯然是引用了PlatformSocket.h,那麼我能在專案中找到這個標頭檔案呢?當然是找不到,不僅PlatformSocket.h找不到,連PlatformSocket.h也找不到。因為他們都沒有新增到我的當前專案,之所以我的專案現在可以解析這些符號,完全是因為我單獨新增外部符號到當前專案的緣故。現在對這個問題再一次抽象

如上圖所示,在使用開源庫的時候,或者處理大型專案的時候,符號衝突在所難免。假設我的專案直接使用標頭檔案A.h和D.h,間接實用C.h中的THRIFT_SOCKET。上圖為了簡化,A.h直接使用B.h,現實問題中情況可能比這個還複雜,中間會經歷更多的標頭檔案。在構建專案的時候,就會出現THRIFT_SOCKET符號多次定義的問題。由於C.h時系統標準路徑下的程式碼,你一般不太敢改動,而D.h中的程式碼是你自己寫的,於是這個時候為了順利構建以及照顧系統程式碼你可能就會更改自己的程式碼。如果你的程式碼中針對THRIFT_SOCKET符號有者複雜的處理邏輯,恐怕你改起自己的程式碼來也會很不爽(以我遇到的問題為例,你可以把typedef THRIFT_SOCKET evutil_socket_t;註釋掉)。

進一步講,假如THRIFT_SOCKET多次定義的位置都是位於系統檔案的地方,如果不改變程式碼那似乎就是死路一條了。我們真的沒有辦法了嗎?並不是,可以在專案構建起始階段來解決這個問題,就像我最開始那樣,在CMakefilelist.txt中規避問題。