2011-11-02 80 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中选择的答案类似,但不完全相同,因为间谍及其预处理程序指令是在可以包含在任何单元测试中的单独文件中定义的,因此不需要将替换注册到特定的头文件。