在静态库中重写C函数

Overiding C functions in static library

本文关键字:函数 重写 静态      更新时间:2023-10-16

我得到多个定义错误,不明白为什么。

我在Cygwin上用g++编译C代码。我正在使用单元测试框架(谷歌测试,这是c++,这就是为什么我不使用gcc)。单元测试源文件#包含包含我想要进行单元测试的函数的.c文件(fileA.c),实质上使其成为该文件的扩展,然后编译单元测试源文件。被测试的函数调用在fileB.h中声明并在fileB.c中定义的函数。这有点像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的符号被链接到一个包含所有生产代码的静态库中。静态库是用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时失败。使用libmylib。它使用-static标志。我尝试了一些我在stackoverflow的其他地方看到的建议,比如-z muldefs,但是Cygwin不喜欢这样,我真的很想了解发生了什么。

多个定义的原因是fileB.h中的"extern pClass Object1"。作为一个未解析的外部对象,当对象文件包含它时,链接器会将定义它的对象拉入,即fileB.o。链接器拉入整个文件b。而不仅仅是一个符号,所以函数定义也被拉了进来。因此,当单元测试代码编译并具有spy定义时,它编译得很好。但是当链接器试图在静态库中链接时,它会发现另一个定义,链接失败。

没有尝试通过让链接器首先找到spy版本来覆盖静态库中的函数调用,我最终给了spy一个稍微不同的名称,并使用预处理器将其交换到单元测试中。这允许产品代码保持不变。它与C中Override a function call中选择的答案类似,但不完全相同,因为spy及其预处理器指令是在一个单独的文件中定义的,该文件可以包含在任何单元测试中,因此不需要在特定的头文件中注册替换。