2011-11-02 79 views
-1

我收到多個定義錯誤,不明白爲什麼。靜態庫中的過度C函數

我在Cygwin上用g ++編譯C代碼。我正在使用單元測試框架(谷歌測試,這是C + +,這就是爲什麼我不使用GCC)。單元測試源文件#包含包含我想單元測試的函數的.c文件(fileA.c),本質上使其成爲所述文件的擴展,並且我編譯單元測試源文件。被測函數調用在fileB.h中聲明並在fileB.c中定義的函數。這有點像Override a function call in C。我沒有展示包括警衛,但本質上剝離下來的代碼如下所示:

fileB.h

typedef void * pClass; // to hide the "class" in the .c file 
extern pClass pObject1; 

void do_something_with_object(void *); 

fileB.c

#include "FileB.h" 

typedef struct myclass { 
    int stuff; 
} myclass; 

myclass Obj1 = { initial_value }; 

void *pObj1 = &Obj1; 

void do_something_with_object(void *arg) { 
    // do stuff, casting to myclass and verifying it's the right kind 
} 

fileA.h

#include "fileB.h" 

void myfunc(void); 

fileA.c

#include "fileA.h" 

void myfunc(void) { 

    do_something_with_object(pObj1); 
} 

utest_fileA.cpp

#include "../fileA.c" 
// google test infrastructure stuff not shown 
TEST(testname, testcase) { 
    myfunc(); 
} 

FILEB的sybols被鏈接到包含所有的生產代碼的靜態庫。靜態庫是用g ++編譯的,所以沒有extern "C"問題。 utest_fileA.cpp的makefile在這個庫中靜態鏈接。到現在爲止還挺好。現在,我想提供我自己的假版本的do_something_with_object,以便我可以監視傳入的內容,以確保myfunc使用正確的參數調用do_something_with_object。我不想修改生產代碼來支持這個單元測試。所以,我創建do_something_with_object仿版進一步向上的編譯單元爲我的單元測試來源:

static void * myspy; 
void do_something_with_object(void * arg) { 
    myspy = arg; 
} 

的想法是,單元測試將使用假冒的定義,因爲它有一個定義,它贏得了」不要在靜態庫中尋找它,並且不會有衝突。通常這是有效的。但是現在我遇到了這種情況,我得到了do_something_with_object()的多個定義錯誤,首先找到假的,然後是真實的。我沒有看到與這種情況有什麼不同,但顯然我錯過了一些東西。什麼可能導致這個?我應該尋找什麼?嘗試將utest_fileA.o與libMyLib.a鏈接時失敗。它使用-static標誌。我嘗試了一些我在其他地方看到的在stackoverflow上建議的東西,比如-z muldefs,但是Cygwin不喜歡這個,我真的很想知道發生了什麼。

+3

您包含'fake_fileB.c'而不是'fake_fileB.h'? –

+0

不要這麼倉促。既然你沒有看到我在做什麼或問什麼,我會簡化這個例子。 – jasper77

+0

我不會'typedef void * pClass;'因爲那麼你還必須執行'typedef const void * cpClass;',然後你的用戶必須記住''const'指針typedef使用的命名約定。 (IMHO)直接使用'void *'或'const void *',或者說'typedef void Class;'並使用Class *和const Class *(至少_looks_說明)。 –

回答

0

多重定義的原因是fileB.h中的「extern pClass Object1」。作爲一個未解決的外部問題,當一個目標文件包含它時,鏈接器將引入它定義的對象,即fileB.o。鏈接器引入整個文件.o而不僅僅是一個符號,所以函數定義也被引入。因此,當單元測試代碼編譯並具有間諜定義時,它編譯得很好。但是當鏈接器嘗試鏈接靜態庫時,它會發現另一個定義,並且鏈接失敗。

而不是試圖通過讓鏈接器首先找到間諜版本重寫靜態庫中的函數調用,我最終給了間諜一個稍微不同的名稱,並使用預處理器交換它進行單元測試。這使生產代碼保持不變。這與Override a function call in C中選擇的答案類似,但不完全相同,因爲間諜及其預處理程序指令是在可以包含在任何單元測試中的單獨文件中定義的,因此不需要將替換註冊到特定的頭文件。